diff options
author | jaseg <git@jaseg.de> | 2023-10-14 14:54:16 +0200 |
---|---|---|
committer | jaseg <git@jaseg.de> | 2023-10-14 14:54:16 +0200 |
commit | d0299874339b48e9f08b063d5864914c07700156 (patch) | |
tree | e1173ffb785930b6967f50e0bc75b72fdc2c4556 | |
parent | f87afaaf44ad9a04dc1434b88db1158b0afc7008 (diff) | |
parent | d3129f384f2a7313d7d2259b09c4d46661597ed7 (diff) | |
download | blog-d0299874339b48e9f08b063d5864914c07700156.tar.gz blog-d0299874339b48e9f08b063d5864914c07700156.tar.bz2 blog-d0299874339b48e9f08b063d5864914c07700156.zip |
deploy.py auto-commit
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | .gitmodules | 0 | ||||
-rw-r--r-- | about/index.html | 98 | ||||
-rw-r--r-- | archetypes/default.md | 6 | ||||
-rw-r--r-- | blog/hsm-basics/index.html | 240 | ||||
-rw-r--r-- | blog/hsm-basics/mori_semi_hsm_talk_web.pdf (renamed from content/blog/hsm-basics/mori_semi_hsm_talk_web.pdf) | bin | 1075912 -> 1075912 bytes | |||
-rw-r--r-- | blog/ihsm-worlds-first-diy-hsm/index.html | 81 | ||||
-rw-r--r-- | blog/index.html | 139 | ||||
-rw-r--r-- | blog/index.xml | 105 | ||||
-rw-r--r--[-rwxr-xr-x] | blog/kicad-mesh-plugin/images/anim.webp (renamed from content/blog/kicad-mesh-plugin/images/anim.webp) | bin | 35626 -> 35626 bytes | |||
-rw-r--r--[-rwxr-xr-x] | blog/kicad-mesh-plugin/images/cells-0.svg (renamed from content/blog/kicad-mesh-plugin/images/cells-0.svg) | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | blog/kicad-mesh-plugin/images/cells-100.svg (renamed from content/blog/kicad-mesh-plugin/images/cells-100.svg) | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | blog/kicad-mesh-plugin/images/cells-25.svg (renamed from content/blog/kicad-mesh-plugin/images/cells-25.svg) | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | blog/kicad-mesh-plugin/images/cells-50.svg (renamed from content/blog/kicad-mesh-plugin/images/cells-50.svg) | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | blog/kicad-mesh-plugin/images/cells-75.svg (renamed from content/blog/kicad-mesh-plugin/images/cells-75.svg) | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | blog/kicad-mesh-plugin/images/grid-vis-plain.svg (renamed from content/blog/kicad-mesh-plugin/images/grid-vis-plain.svg) | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | blog/kicad-mesh-plugin/images/grid-vis.svg (renamed from content/blog/kicad-mesh-plugin/images/grid-vis.svg) | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | blog/kicad-mesh-plugin/images/kicad-mesh-outline.png (renamed from content/blog/kicad-mesh-plugin/images/kicad-mesh-outline.png) | bin | 120770 -> 120770 bytes | |||
-rw-r--r--[-rwxr-xr-x] | blog/kicad-mesh-plugin/images/kicad-mesh-result-large.png (renamed from content/blog/kicad-mesh-plugin/images/kicad-mesh-result-large.png) | bin | 197766 -> 197766 bytes | |||
-rw-r--r--[-rwxr-xr-x] | blog/kicad-mesh-plugin/images/kicad-mesh-settings.png (renamed from content/blog/kicad-mesh-plugin/images/kicad-mesh-settings.png) | bin | 46346 -> 46346 bytes | |||
-rw-r--r--[-rwxr-xr-x] | blog/kicad-mesh-plugin/images/kicad-mesh-settings2.png (renamed from content/blog/kicad-mesh-plugin/images/kicad-mesh-settings2.png) | bin | 131267 -> 131267 bytes | |||
-rw-r--r--[-rwxr-xr-x] | blog/kicad-mesh-plugin/images/maze_tiles.svg (renamed from content/blog/kicad-mesh-plugin/images/maze_tiles.svg) | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | blog/kicad-mesh-plugin/images/maze_tiles_plain.svg (renamed from content/blog/kicad-mesh-plugin/images/maze_tiles_plain.svg) | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | blog/kicad-mesh-plugin/images/modern_art.svg (renamed from content/blog/kicad-mesh-plugin/images/modern_art.svg) | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | blog/kicad-mesh-plugin/images/tiles-25-small.svg (renamed from content/blog/kicad-mesh-plugin/images/tiles-25-small.svg) | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | blog/kicad-mesh-plugin/images/traces-25-small.svg (renamed from content/blog/kicad-mesh-plugin/images/traces-25-small.svg) | 0 | ||||
-rw-r--r-- | blog/kicad-mesh-plugin/index.html | 217 | ||||
-rw-r--r-- | blog/led-characterization/images/daylight_spectrum_dvd.jpg (renamed from content/blog/led-characterization/images/daylight_spectrum_dvd.jpg) | bin | 79150 -> 79150 bytes | |||
-rw-r--r-- | blog/led-characterization/images/driver_ringing_strong.jpg (renamed from content/blog/led-characterization/images/driver_ringing_strong.jpg) | bin | 285952 -> 285952 bytes | |||
-rw-r--r-- | blog/led-characterization/images/driver_ringing_weak.jpg (renamed from content/blog/led-characterization/images/driver_ringing_weak.jpg) | bin | 292100 -> 292100 bytes | |||
-rw-r--r-- | blog/led-characterization/images/electronics_whole.jpg (renamed from content/blog/led-characterization/images/electronics_whole.jpg) | bin | 438776 -> 438776 bytes | |||
-rw-r--r-- | blog/led-characterization/images/hsv_cylinder.png (renamed from content/blog/led-characterization/images/hsv_cylinder.png) | bin | 293981 -> 293981 bytes | |||
-rw-r--r-- | blog/led-characterization/images/photodiode_sensitivity.svg (renamed from content/blog/led-characterization/images/photodiode_sensitivity.svg) | 0 | ||||
-rw-r--r-- | blog/led-characterization/images/preamp_back.jpg (renamed from content/blog/led-characterization/images/preamp_back.jpg) | bin | 340446 -> 340446 bytes | |||
-rw-r--r-- | blog/led-characterization/images/preamp_front.jpg (renamed from content/blog/led-characterization/images/preamp_front.jpg) | bin | 308167 -> 308167 bytes | |||
-rw-r--r-- | blog/led-characterization/images/preamp_schematic.jpg (renamed from content/blog/led-characterization/images/preamp_schematic.jpg) | bin | 277414 -> 277414 bytes | |||
-rw-r--r-- | blog/led-characterization/images/processed_plot_cheap_rgb.svg (renamed from content/blog/led-characterization/images/processed_plot_cheap_rgb.svg) | 0 | ||||
-rw-r--r-- | blog/led-characterization/images/raw_plot_cheap_rgb.svg (renamed from content/blog/led-characterization/images/raw_plot_cheap_rgb.svg) | 0 | ||||
-rw-r--r-- | blog/led-characterization/images/rgb_cube.svg (renamed from content/blog/led-characterization/images/rgb_cube.svg) | 0 | ||||
-rw-r--r-- | blog/led-characterization/images/spectrograph_step1_parts.jpg (renamed from content/blog/led-characterization/images/spectrograph_step1_parts.jpg) | bin | 284254 -> 284254 bytes | |||
-rw-r--r-- | blog/led-characterization/images/spectrograph_step2.jpg (renamed from content/blog/led-characterization/images/spectrograph_step2.jpg) | bin | 277234 -> 277234 bytes | |||
-rw-r--r-- | blog/led-characterization/images/spectrograph_step3.jpg (renamed from content/blog/led-characterization/images/spectrograph_step3.jpg) | bin | 256192 -> 256192 bytes | |||
-rw-r--r-- | blog/led-characterization/images/spectrograph_step4_complete.jpg (renamed from content/blog/led-characterization/images/spectrograph_step4_complete.jpg) | bin | 303777 -> 303777 bytes | |||
-rw-r--r-- | blog/led-characterization/images/zeus_hammer_breadboard.jpg (renamed from content/blog/led-characterization/images/zeus_hammer_breadboard.jpg) | bin | 445768 -> 445768 bytes | |||
-rw-r--r-- | blog/led-characterization/images/zeus_hammer_breadboard_original.jpg (renamed from content/blog/led-characterization/images/zeus_hammer_breadboard_original.jpg) | bin | 2474273 -> 2474273 bytes | |||
-rw-r--r-- | blog/led-characterization/images/zeus_hammer_schematic.jpg (renamed from content/blog/led-characterization/images/zeus_hammer_schematic.jpg) | bin | 148056 -> 148056 bytes | |||
-rw-r--r-- | blog/led-characterization/images/zeus_hammer_schematic_original.jpg (renamed from content/blog/led-characterization/images/zeus_hammer_schematic_original.jpg) | bin | 1893757 -> 1893757 bytes | |||
-rw-r--r-- | blog/led-characterization/index.html | 431 | ||||
-rw-r--r-- | blog/led-characterization/video/led_within_srgb_fancy_camera_path_scale=2.5.mkv (renamed from content/blog/led-characterization/video/led_within_srgb_fancy_camera_path_scale=2.5.mkv) | bin | 2676955 -> 2676955 bytes | |||
-rw-r--r-- | blog/led-characterization/video/led_within_srgb_fancy_camera_path_scale=2.5.webm (renamed from content/blog/led-characterization/video/led_within_srgb_fancy_camera_path_scale=2.5.webm) | bin | 1822963 -> 1822963 bytes | |||
-rw-r--r-- | blog/led-characterization/video/led_within_srgb_scale=1.0.mkv (renamed from content/blog/led-characterization/video/led_within_srgb_scale=1.0.mkv) | bin | 1223799 -> 1223799 bytes | |||
-rw-r--r-- | blog/led-characterization/video/led_within_srgb_scale=1.0.webm (renamed from content/blog/led-characterization/video/led_within_srgb_scale=1.0.webm) | bin | 789099 -> 789099 bytes | |||
-rw-r--r-- | blog/led-characterization/video/led_within_srgb_scale=2.5.mkv (renamed from content/blog/led-characterization/video/led_within_srgb_scale=2.5.mkv) | bin | 1559120 -> 1559120 bytes | |||
-rw-r--r-- | blog/led-characterization/video/led_within_srgb_scale=2.5.webm (renamed from content/blog/led-characterization/video/led_within_srgb_scale=2.5.webm) | bin | 920089 -> 920089 bytes | |||
-rw-r--r-- | blog/led-characterization/video/led_within_srgb_scale=3.mkv (renamed from content/blog/led-characterization/video/led_within_srgb_scale=3.mkv) | bin | 1709475 -> 1709475 bytes | |||
-rw-r--r-- | blog/led-characterization/video/led_within_srgb_scale=3.webm (renamed from content/blog/led-characterization/video/led_within_srgb_scale=3.webm) | bin | 925593 -> 925593 bytes | |||
-rw-r--r-- | blog/led-characterization/video/sRGB.mkv (renamed from content/blog/led-characterization/video/sRGB.mkv) | bin | 1269287 -> 1269287 bytes | |||
-rw-r--r-- | blog/led-characterization/video/sRGB.webm (renamed from content/blog/led-characterization/video/sRGB.webm) | bin | 791821 -> 791821 bytes | |||
-rw-r--r-- | blog/led-characterization/video/scale=1.mkv (renamed from content/blog/led-characterization/video/scale=1.mkv) | bin | 1011548 -> 1011548 bytes | |||
-rw-r--r-- | blog/led-characterization/video/scale=1.webm (renamed from content/blog/led-characterization/video/scale=1.webm) | bin | 758345 -> 758345 bytes | |||
-rw-r--r-- | blog/led-characterization/video/scale=2.5.mkv (renamed from content/blog/led-characterization/video/scale=2.5.mkv) | bin | 1296564 -> 1296564 bytes | |||
-rw-r--r-- | blog/led-characterization/video/scale=2.5.webm (renamed from content/blog/led-characterization/video/scale=2.5.webm) | bin | 882674 -> 882674 bytes | |||
-rw-r--r-- | blog/led-characterization/video/scale=5.mkv (renamed from content/blog/led-characterization/video/scale=5.mkv) | bin | 1571645 -> 1571645 bytes | |||
-rw-r--r-- | blog/led-characterization/video/scale=5.webm (renamed from content/blog/led-characterization/video/scale=5.webm) | bin | 879476 -> 879476 bytes | |||
-rw-r--r-- | blog/multichannel-led-driver/images/asymmetric_iled.svg (renamed from content/blog/multichannel-led-driver/images/asymmetric_iled.svg) | 0 | ||||
-rw-r--r-- | blog/multichannel-led-driver/images/asymmetric_vgate.svg (renamed from content/blog/multichannel-led-driver/images/asymmetric_vgate.svg) | 0 | ||||
-rw-r--r-- | blog/multichannel-led-driver/images/bcm_schema.jpg (renamed from content/blog/multichannel-led-driver/images/bcm_schema.jpg) | bin | 1626022 -> 1626022 bytes | |||
-rw-r--r-- | blog/multichannel-led-driver/images/corrected_brightness_sim.svg (renamed from content/blog/multichannel-led-driver/images/corrected_brightness_sim.svg) | 0 | ||||
-rw-r--r-- | blog/multichannel-led-driver/images/driver_linearity_raw.svg (renamed from content/blog/multichannel-led-driver/images/driver_linearity_raw.svg) | 0 | ||||
-rw-r--r-- | blog/multichannel-led-driver/images/driver_output_ltspice_schematic.jpg (renamed from content/blog/multichannel-led-driver/images/driver_output_ltspice_schematic.jpg) | bin | 876019 -> 876019 bytes | |||
-rw-r--r-- | blog/multichannel-led-driver/images/driver_pcb_built.jpg (renamed from content/blog/multichannel-led-driver/images/driver_pcb_built.jpg) | bin | 1229840 -> 1229840 bytes | |||
-rw-r--r-- | blog/multichannel-led-driver/images/driver_ringing_strong.jpg (renamed from content/blog/multichannel-led-driver/images/driver_ringing_strong.jpg) | bin | 285952 -> 285952 bytes | |||
-rw-r--r-- | blog/multichannel-led-driver/images/driver_ringing_weak.jpg (renamed from content/blog/multichannel-led-driver/images/driver_ringing_weak.jpg) | bin | 292100 -> 292100 bytes | |||
-rw-r--r-- | blog/multichannel-led-driver/images/led_strip_alight.jpg (renamed from content/blog/multichannel-led-driver/images/led_strip_alight.jpg) | bin | 1752106 -> 1752106 bytes | |||
-rw-r--r-- | blog/multichannel-led-driver/images/linearization_setup.jpg (renamed from content/blog/multichannel-led-driver/images/linearization_setup.jpg) | bin | 2038436 -> 2038436 bytes | |||
-rw-r--r-- | blog/multichannel-led-driver/images/olsndot_output_schematic.jpg (renamed from content/blog/multichannel-led-driver/images/olsndot_output_schematic.jpg) | bin | 1410898 -> 1410898 bytes | |||
-rw-r--r-- | blog/multichannel-led-driver/images/olsndot_pcb.png (renamed from content/blog/multichannel-led-driver/images/olsndot_pcb.png) | bin | 137479 -> 137479 bytes | |||
-rw-r--r-- | blog/multichannel-led-driver/images/olsndot_schematic.png (renamed from content/blog/multichannel-led-driver/images/olsndot_schematic.png) | bin | 362216 -> 362216 bytes | |||
-rw-r--r-- | blog/multichannel-led-driver/images/overshoot_sim_r0.svg (renamed from content/blog/multichannel-led-driver/images/overshoot_sim_r0.svg) | 0 | ||||
-rw-r--r-- | blog/multichannel-led-driver/images/overshoot_sim_r100.svg (renamed from content/blog/multichannel-led-driver/images/overshoot_sim_r100.svg) | 0 | ||||
-rw-r--r-- | blog/multichannel-led-driver/images/pwm_schema.jpg (renamed from content/blog/multichannel-led-driver/images/pwm_schema.jpg) | bin | 840134 -> 840134 bytes | |||
-rw-r--r-- | blog/multichannel-led-driver/images/uncorrected_brightness_sim.svg (renamed from content/blog/multichannel-led-driver/images/uncorrected_brightness_sim.svg) | 0 | ||||
-rw-r--r-- | blog/multichannel-led-driver/index.html | 384 | ||||
-rw-r--r-- | blog/multichannel-led-driver/olsndot_v02_schematics_and_pcb.pdf (renamed from content/blog/multichannel-led-driver/olsndot_v02_schematics_and_pcb.pdf) | bin | 1036117 -> 1036117 bytes | |||
-rw-r--r-- | blog/private-contact-discovery/index.html | 80 | ||||
-rw-r--r-- | blog/private-contact-discovery/mori_semi_psi_talk.odp (renamed from content/blog/private-contact-discovery/mori_semi_psi_talk.odp) | bin | 35999576 -> 35999576 bytes | |||
-rw-r--r-- | blog/private-contact-discovery/mori_semi_psi_talk.pdf (renamed from content/blog/private-contact-discovery/mori_semi_psi_talk.pdf) | bin | 11600295 -> 11600295 bytes | |||
-rw-r--r-- | blog/serial-protocols/index.html | 251 | ||||
-rw-r--r-- | blog/telekom-gpon-sfp/images/edgerouter_interface_config.png (renamed from content/blog/telekom-gpon-sfp/images/edgerouter_interface_config.png) | bin | 148433 -> 148433 bytes | |||
-rw-r--r-- | blog/telekom-gpon-sfp/images/edgerouter_route_config.png (renamed from content/blog/telekom-gpon-sfp/images/edgerouter_route_config.png) | bin | 75601 -> 75601 bytes | |||
-rw-r--r-- | blog/telekom-gpon-sfp/images/edgerouter_sfp_config.png (renamed from content/blog/telekom-gpon-sfp/images/edgerouter_sfp_config.png) | bin | 56138 -> 56138 bytes | |||
-rw-r--r-- | blog/telekom-gpon-sfp/images/edgerouter_snat_config.png (renamed from content/blog/telekom-gpon-sfp/images/edgerouter_snat_config.png) | bin | 118370 -> 118370 bytes | |||
-rw-r--r-- | blog/telekom-gpon-sfp/images/edgerouter_snat_config2.png (renamed from content/blog/telekom-gpon-sfp/images/edgerouter_snat_config2.png) | bin | 82458 -> 82458 bytes | |||
-rw-r--r-- | blog/telekom-gpon-sfp/images/sfp_onu_ploam_pw_config.png (renamed from content/blog/telekom-gpon-sfp/images/sfp_onu_ploam_pw_config.png) | bin | 152023 -> 152023 bytes | |||
-rw-r--r-- | blog/telekom-gpon-sfp/images/sfp_onu_reset.png (renamed from content/blog/telekom-gpon-sfp/images/sfp_onu_reset.png) | bin | 132106 -> 132106 bytes | |||
-rw-r--r-- | blog/telekom-gpon-sfp/images/sfp_onu_web_if.png (renamed from content/blog/telekom-gpon-sfp/images/sfp_onu_web_if.png) | bin | 133838 -> 133838 bytes | |||
-rw-r--r-- | blog/telekom-gpon-sfp/index.html | 226 | ||||
-rw-r--r-- | blog/thors-hammer/images/thors_hammer_breadboard.jpg (renamed from content/blog/thors-hammer/images/thors_hammer_breadboard.jpg) | bin | 3229178 -> 3229178 bytes | |||
-rw-r--r-- | blog/thors-hammer/images/thors_hammer_schematic.jpg (renamed from content/blog/thors-hammer/images/thors_hammer_schematic.jpg) | bin | 1822573 -> 1822573 bytes | |||
-rw-r--r-- | blog/thors-hammer/index.html | 88 | ||||
-rw-r--r-- | blog/thors-hammer/video/thors_hammer.mkv (renamed from content/blog/thors-hammer/video/thors_hammer.mkv) | bin | 3799797 -> 3799797 bytes | |||
-rw-r--r-- | blog/thors-hammer/video/thors_hammer.mov (renamed from content/blog/thors-hammer/video/thors_hammer.mov) | bin | 33979697 -> 33979697 bytes | |||
-rw-r--r-- | blog/thors-hammer/video/thors_hammer.webm (renamed from content/blog/thors-hammer/video/thors_hammer.webm) | bin | 4868825 -> 4868825 bytes | |||
-rw-r--r-- | blog/wifi-led-driver/images/board_in_case.jpg (renamed from content/blog/wifi-led-driver/images/board_in_case.jpg) | bin | 2898747 -> 2898747 bytes | |||
-rw-r--r-- | blog/wifi-led-driver/images/board_in_case.small.jpg (renamed from content/blog/wifi-led-driver/images/board_in_case.small.jpg) | bin | 817789 -> 817789 bytes | |||
-rw-r--r-- | blog/wifi-led-driver/images/boards.jpg (renamed from content/blog/wifi-led-driver/images/boards.jpg) | bin | 3642485 -> 3642485 bytes | |||
-rw-r--r-- | blog/wifi-led-driver/images/boards.small.jpg (renamed from content/blog/wifi-led-driver/images/boards.small.jpg) | bin | 865465 -> 865465 bytes | |||
-rw-r--r-- | blog/wifi-led-driver/images/layout.png (renamed from content/blog/wifi-led-driver/images/layout.png) | bin | 114048 -> 114048 bytes | |||
-rw-r--r-- | blog/wifi-led-driver/images/schematic.png (renamed from content/blog/wifi-led-driver/images/schematic.png) | bin | 301982 -> 301982 bytes | |||
-rw-r--r-- | blog/wifi-led-driver/index.html | 146 | ||||
-rw-r--r-- | blog/wifi-led-driver/resource/lyza_schematic_and_pcb.pdf (renamed from content/blog/wifi-led-driver/resource/lyza_schematic_and_pcb.pdf) | bin | 781457 -> 781457 bytes | |||
-rw-r--r-- | categories/index.html | 49 | ||||
-rw-r--r-- | categories/index.xml | 11 | ||||
-rw-r--r-- | config.toml | 74 | ||||
-rw-r--r-- | content/_index.rst | 9 | ||||
-rw-r--r-- | content/about/index.rst | 63 | ||||
-rw-r--r-- | content/blog/_index.rst | 3 | ||||
-rw-r--r-- | content/blog/hsm-basics/index.rst | 214 | ||||
-rw-r--r-- | content/blog/ihsm-worlds-first-diy-hsm/index.rst | 41 | ||||
-rw-r--r-- | content/blog/kicad-mesh-plugin/index.rst | 225 | ||||
-rw-r--r-- | content/blog/led-characterization/index.rst | 504 | ||||
-rw-r--r-- | content/blog/multichannel-led-driver/index.rst | 458 | ||||
-rw-r--r-- | content/blog/private-contact-discovery/index.rst | 38 | ||||
-rw-r--r-- | content/blog/serial-protocols/index.rst | 249 | ||||
-rwxr-xr-x | content/blog/sybil-resistance-identity/images/succulents.jpg | bin | 587685 -> 0 bytes | |||
-rw-r--r-- | content/blog/sybil-resistance-identity/index-old.rst | 244 | ||||
-rw-r--r-- | content/blog/sybil-resistance-identity/index.rst | 89 | ||||
-rw-r--r-- | content/blog/telekom-gpon-sfp/index.rst | 216 | ||||
-rw-r--r-- | content/blog/thors-hammer/index.rst | 60 | ||||
-rw-r--r-- | content/blog/wifi-led-driver/index.rst | 146 | ||||
-rw-r--r-- | content/imprint/index.rst | 102 | ||||
-rw-r--r-- | content/projects/_index.rst | 7 | ||||
-rw-r--r-- | content/projects/gerbolyze/README.rst | 700 | ||||
-rw-r--r-- | content/projects/gerbolyze/index.rst | 17 | ||||
-rw-r--r-- | content/projects/gerbonara/index.rst | 141 | ||||
-rw-r--r-- | content/projects/kimesh/README.rst | 64 | ||||
-rw-r--r-- | content/projects/kimesh/index.rst | 16 | ||||
-rw-r--r-- | content/projects/lolcat-c/index.rst | 107 | ||||
-rw-r--r-- | content/projects/python-mpv/README.rst | 401 | ||||
-rw-r--r-- | content/projects/python-mpv/index.rst | 18 | ||||
-rw-r--r-- | content/projects/svg-flatten/index.rst | 22 | ||||
-rw-r--r-- | content/projects/wsdiff/index.rst | 63 | ||||
-rw-r--r-- | deploy.py | 29 | ||||
-rw-r--r-- | docutils.conf | 3 | ||||
-rw-r--r-- | fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Medium.ttf (renamed from themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Medium.ttf) | bin | 52680 -> 52680 bytes | |||
-rw-r--r-- | fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-MediumItalic.ttf (renamed from themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-MediumItalic.ttf) | bin | 57176 -> 57176 bytes | |||
-rw-r--r-- | fonts/fira_code/FiraCode-VariableFont_wght.ttf (renamed from themes/conspiracy/assets/fonts/fira_code/FiraCode-VariableFont_wght.ttf) | bin | 259388 -> 259388 bytes | |||
-rw-r--r-- | fonts/manuskript_gothisch/Manuskript Gothisch UNZ1A.ttf (renamed from themes/conspiracy/assets/fonts/manuskript_gothisch/Manuskript Gothisch UNZ1A.ttf) | bin | 77072 -> 77072 bytes | |||
-rw-r--r-- | fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2 (renamed from themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2) | bin | 37088 -> 37088 bytes | |||
-rw-r--r-- | fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2 (renamed from themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2) | bin | 38412 -> 38412 bytes | |||
-rw-r--r-- | fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Dark.woff2 (renamed from themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Dark.woff2) | bin | 36220 -> 36220 bytes | |||
-rw-r--r-- | fonts/nyght-serif-main/fonts/WEB/NyghtSerif-DarkItalic.woff2 (renamed from themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-DarkItalic.woff2) | bin | 37440 -> 37440 bytes | |||
-rw-r--r-- | fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Light.woff2 (renamed from themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Light.woff2) | bin | 34848 -> 34848 bytes | |||
-rw-r--r-- | fonts/nyght-serif-main/fonts/WEB/NyghtSerif-LightItalic.woff2 (renamed from themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-LightItalic.woff2) | bin | 36288 -> 36288 bytes | |||
-rw-r--r-- | fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Medium.woff2 (renamed from themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Medium.woff2) | bin | 36092 -> 36092 bytes | |||
-rw-r--r-- | fonts/nyght-serif-main/fonts/WEB/NyghtSerif-MediumItalic.woff2 (renamed from themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-MediumItalic.woff2) | bin | 38004 -> 38004 bytes | |||
-rw-r--r-- | fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2 (renamed from themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2) | bin | 36344 -> 36344 bytes | |||
-rw-r--r-- | fonts/nyght-serif-main/fonts/WEB/NyghtSerif-RegularItalic.woff2 (renamed from themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-RegularItalic.woff2) | bin | 38160 -> 38160 bytes | |||
-rw-r--r-- | fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf (renamed from themes/conspiracy/assets/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf) | bin | 247036 -> 247036 bytes | |||
-rwxr-xr-x | hack/rst2html | 50 | ||||
-rw-r--r-- | images/fabrizio-conti-TUmjK7ZJgbI-unsplash.jpg (renamed from themes/conspiracy/assets/images/fabrizio-conti-TUmjK7ZJgbI-unsplash.jpg) | bin | 7393648 -> 7393648 bytes | |||
-rw-r--r-- | imprint/index.html | 130 | ||||
-rw-r--r-- | index.html | 184 | ||||
-rw-r--r-- | index.xml | 215 | ||||
-rw-r--r-- | posts/index.html | 49 | ||||
-rw-r--r-- | posts/index.xml | 11 | ||||
-rw-r--r-- | projects/gerbolyze/index.html | 650 | ||||
-rw-r--r-- | projects/gerbolyze/pics/ex-flattening.png (renamed from content/projects/gerbolyze/pics/ex-flattening.png) | bin | 47137 -> 47137 bytes | |||
-rw-r--r-- | projects/gerbolyze/pics/ex-intersections.png (renamed from content/projects/gerbolyze/pics/ex-intersections.png) | bin | 48558 -> 48558 bytes | |||
-rw-r--r-- | projects/gerbolyze/pics/ex-strokes.png (renamed from content/projects/gerbolyze/pics/ex-strokes.png) | bin | 67441 -> 67441 bytes | |||
-rw-r--r-- | projects/gerbolyze/pics/ex-svg-joins.png (renamed from content/projects/gerbolyze/pics/ex-svg-joins.png) | bin | 18659 -> 18659 bytes | |||
-rw-r--r-- | projects/gerbolyze/pics/ex-svg-strokes.png (renamed from content/projects/gerbolyze/pics/ex-svg-strokes.png) | bin | 67441 -> 67441 bytes | |||
-rw-r--r-- | projects/gerbolyze/pics/ex-svg-winding.png (renamed from content/projects/gerbolyze/pics/ex-svg-winding.png) | bin | 34356 -> 34356 bytes | |||
-rw-r--r-- | projects/gerbolyze/pics/fr4_comparison2.jpg (renamed from content/projects/gerbolyze/pics/fr4_comparison2.jpg) | bin | 271943 -> 271943 bytes | |||
-rw-r--r-- | projects/gerbolyze/pics/pcbway_sample_01_small.jpg (renamed from content/projects/gerbolyze/pics/pcbway_sample_01_small.jpg) | bin | 453117 -> 453117 bytes | |||
-rw-r--r-- | projects/gerbolyze/pics/pcbway_sample_02_small.jpg (renamed from content/projects/gerbolyze/pics/pcbway_sample_02_small.jpg) | bin | 521032 -> 521032 bytes | |||
-rw-r--r-- | projects/gerbolyze/pics/pcbway_sample_03_small.jpg (renamed from content/projects/gerbolyze/pics/pcbway_sample_03_small.jpg) | bin | 746718 -> 746718 bytes | |||
-rw-r--r-- | projects/gerbolyze/pics/process-overview.png (renamed from content/projects/gerbolyze/pics/process-overview.png) | bin | 516640 -> 516640 bytes | |||
-rw-r--r-- | projects/gerbolyze/pics/process-overview.svg (renamed from content/projects/gerbolyze/pics/process-overview.svg) | 0 | ||||
-rw-r--r-- | projects/gerbolyze/pics/sample1.jpg (renamed from content/projects/gerbolyze/pics/sample1.jpg) | bin | 299841 -> 299841 bytes | |||
-rw-r--r-- | projects/gerbolyze/pics/sample2.jpg (renamed from content/projects/gerbolyze/pics/sample2.jpg) | bin | 251440 -> 251440 bytes | |||
-rw-r--r-- | projects/gerbolyze/pics/sample3.jpg (renamed from content/projects/gerbolyze/pics/sample3.jpg) | bin | 171160 -> 171160 bytes | |||
-rw-r--r-- | projects/gerbolyze/pics/subtract_example.png (renamed from content/projects/gerbolyze/pics/subtract_example.png) | bin | 237770 -> 237770 bytes | |||
-rw-r--r-- | projects/gerbolyze/pics/test_svg_readme.svg (renamed from content/projects/gerbolyze/pics/test_svg_readme.svg) | 0 | ||||
-rw-r--r-- | projects/gerbolyze/pics/test_svg_readme_composited.png (renamed from content/projects/gerbolyze/pics/test_svg_readme_composited.png) | bin | 1211058 -> 1211058 bytes | |||
-rw-r--r-- | projects/gerbolyze/pics/vec_contours_composited.png (renamed from content/projects/gerbolyze/pics/vec_contours_composited.png) | bin | 40471 -> 40471 bytes | |||
-rw-r--r-- | projects/gerbolyze/pics/vec_hexgrid_composited.png (renamed from content/projects/gerbolyze/pics/vec_hexgrid_composited.png) | bin | 249235 -> 249235 bytes | |||
-rw-r--r-- | projects/gerbolyze/pics/vec_poisson_composited.png (renamed from content/projects/gerbolyze/pics/vec_poisson_composited.png) | bin | 278253 -> 278253 bytes | |||
-rw-r--r-- | projects/gerbolyze/pics/vec_square_composited.png (renamed from content/projects/gerbolyze/pics/vec_square_composited.png) | bin | 201613 -> 201613 bytes | |||
-rw-r--r-- | projects/gerbolyze/screenshots/01import01.png (renamed from content/projects/gerbolyze/screenshots/01import01.png) | bin | 113001 -> 113001 bytes | |||
-rw-r--r-- | projects/gerbolyze/screenshots/02import02.png (renamed from content/projects/gerbolyze/screenshots/02import02.png) | bin | 370420 -> 370420 bytes | |||
-rw-r--r-- | projects/gerbolyze/screenshots/03paste.png (renamed from content/projects/gerbolyze/screenshots/03paste.png) | bin | 152448 -> 152448 bytes | |||
-rw-r--r-- | projects/gerbolyze/screenshots/04scale_cut.png (renamed from content/projects/gerbolyze/screenshots/04scale_cut.png) | bin | 389163 -> 389163 bytes | |||
-rw-r--r-- | projects/gerbolyze/screenshots/05position.png (renamed from content/projects/gerbolyze/screenshots/05position.png) | bin | 373392 -> 373392 bytes | |||
-rw-r--r-- | projects/gerbolyze/screenshots/06grayscale.png (renamed from content/projects/gerbolyze/screenshots/06grayscale.png) | bin | 314223 -> 314223 bytes | |||
-rw-r--r-- | projects/gerbolyze/screenshots/07curve_settings.png (renamed from content/projects/gerbolyze/screenshots/07curve_settings.png) | bin | 40377 -> 40377 bytes | |||
-rw-r--r-- | projects/gerbolyze/screenshots/08curve_cut.png (renamed from content/projects/gerbolyze/screenshots/08curve_cut.png) | bin | 286585 -> 286585 bytes | |||
-rw-r--r-- | projects/gerbolyze/screenshots/09retouch.png (renamed from content/projects/gerbolyze/screenshots/09retouch.png) | bin | 260487 -> 260487 bytes | |||
-rw-r--r-- | projects/gerbolyze/screenshots/10retouched.png (renamed from content/projects/gerbolyze/screenshots/10retouched.png) | bin | 318000 -> 318000 bytes | |||
-rw-r--r-- | projects/gerbolyze/screenshots/11newsprint.png (renamed from content/projects/gerbolyze/screenshots/11newsprint.png) | bin | 1150483 -> 1150483 bytes | |||
-rw-r--r-- | projects/gerbolyze/screenshots/11newsprint_settings.png (renamed from content/projects/gerbolyze/screenshots/11newsprint_settings.png) | bin | 167175 -> 167175 bytes | |||
-rw-r--r-- | projects/gerbolyze/screenshots/12newsprint.png (renamed from content/projects/gerbolyze/screenshots/12newsprint.png) | bin | 581329 -> 581329 bytes | |||
-rw-r--r-- | projects/gerbolyze/screenshots/13newsprint.png (renamed from content/projects/gerbolyze/screenshots/13newsprint.png) | bin | 515144 -> 515144 bytes | |||
-rw-r--r-- | projects/gerbolyze/screenshots/14newsprint.png (renamed from content/projects/gerbolyze/screenshots/14newsprint.png) | bin | 341639 -> 341639 bytes | |||
-rw-r--r-- | projects/gerbolyze/screenshots/14result_cut.png (renamed from content/projects/gerbolyze/screenshots/14result_cut.png) | bin | 117033 -> 117033 bytes | |||
-rw-r--r-- | projects/gerbolyze/screenshots/15result_cut.png (renamed from content/projects/gerbolyze/screenshots/15result_cut.png) | bin | 61558 -> 61558 bytes | |||
-rw-r--r-- | projects/gerbolyze/screenshots/16result_cut.png (renamed from content/projects/gerbolyze/screenshots/16result_cut.png) | bin | 40076 -> 40076 bytes | |||
-rw-r--r-- | projects/gerbolyze/screenshots/17caveat_cut.png (renamed from content/projects/gerbolyze/screenshots/17caveat_cut.png) | bin | 24364 -> 24364 bytes | |||
-rw-r--r-- | projects/gerbolyze/screenshots/18caveat_cut.png (renamed from content/projects/gerbolyze/screenshots/18caveat_cut.png) | bin | 25357 -> 25357 bytes | |||
-rw-r--r-- | projects/gerbonara/index.html | 165 | ||||
-rw-r--r-- | projects/index.html | 180 | ||||
-rw-r--r-- | projects/index.xml | 103 | ||||
-rw-r--r-- | projects/kimesh/index.html | 104 | ||||
-rw-r--r-- | projects/kimesh/kicad-mesh-result-large.png (renamed from content/projects/kimesh/kicad-mesh-result-large.png) | bin | 197766 -> 197766 bytes | |||
-rw-r--r-- | projects/kimesh/screenshot-mesh-schematic.png (renamed from content/projects/kimesh/screenshot-mesh-schematic.png) | bin | 50833 -> 50833 bytes | |||
-rw-r--r-- | projects/lolcat-c/LOLCat-Rainbow.jpg (renamed from content/projects/lolcat-c/LOLCat-Rainbow.jpg) | bin | 42785 -> 42785 bytes | |||
-rw-r--r-- | projects/lolcat-c/index.html | 132 | ||||
-rw-r--r-- | projects/lolcat-c/screenshot.png (renamed from content/projects/lolcat-c/screenshot.png) | bin | 198032 -> 198032 bytes | |||
-rw-r--r-- | projects/lolcat-c/sl.gif (renamed from content/projects/lolcat-c/sl.gif) | bin | 1466027 -> 1466027 bytes | |||
-rw-r--r-- | projects/python-mpv/index.html | 408 | ||||
-rw-r--r-- | projects/svg-flatten/index.html | 63 | ||||
-rw-r--r-- | projects/wsdiff/index.html | 105 | ||||
-rw-r--r-- | projects/wsdiff/latest.png (renamed from content/projects/wsdiff/latest.png) | bin | 136920 -> 136920 bytes | |||
-rw-r--r-- | research/colorspace/Colorspace Visualization.ipynb | 69 | ||||
-rw-r--r-- | research/colorspace/cccie31_locus.csv | 95 | ||||
-rw-r--r-- | research/colorspace/cccie31_locus.svg | 19 | ||||
-rw-r--r-- | research/colorspace/cccie31_locus_edited.png | bin | 70872 -> 0 bytes | |||
-rw-r--r-- | research/colorspace/cccie31_locus_edited.svg | 64 | ||||
-rw-r--r-- | research/colorspace/colorsys_led_mapping.blend | bin | 677860 -> 0 bytes | |||
-rw-r--r-- | research/colorspace/colorsys_led_mapping.blend1 | bin | 677860 -> 0 bytes | |||
-rwxr-xr-x | research/colorspace/csv_to_svg_path.py | 43 | ||||
-rw-r--r-- | sitemap.xml | 67 | ||||
-rw-r--r-- | style.css (renamed from themes/conspiracy/assets/css/style.css) | 34 | ||||
-rw-r--r-- | themes/conspiracy/LICENSE | 20 | ||||
-rw-r--r-- | themes/conspiracy/archetypes/default.md | 2 | ||||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/BodoniModa-Italic-VariableFont_opsz,wght.ttf | bin | 169892 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/BodoniModa-VariableFont_opsz,wght.ttf | bin | 151248 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/OFL.txt | 93 | ||||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/README.txt | 124 | ||||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Black.ttf | bin | 52792 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-BlackItalic.ttf | bin | 57360 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Bold.ttf | bin | 52768 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-BoldItalic.ttf | bin | 57272 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-ExtraBold.ttf | bin | 52884 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-ExtraBoldItalic.ttf | bin | 57368 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Italic.ttf | bin | 57044 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-MediumItalic.ttf | bin | 57212 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Regular.ttf | bin | 52644 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-SemiBold.ttf | bin | 52760 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-SemiBoldItalic.ttf | bin | 57296 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-Black.ttf | bin | 52784 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-BlackItalic.ttf | bin | 57352 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-Bold.ttf | bin | 52780 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-BoldItalic.ttf | bin | 57268 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-ExtraBold.ttf | bin | 52852 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-ExtraBoldItalic.ttf | bin | 57384 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-Italic.ttf | bin | 57048 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-Medium.ttf | bin | 52700 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-MediumItalic.ttf | bin | 57176 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-Regular.ttf | bin | 52608 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-SemiBold.ttf | bin | 52768 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-SemiBoldItalic.ttf | bin | 57292 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-Black.ttf | bin | 52808 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-BlackItalic.ttf | bin | 57340 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-Bold.ttf | bin | 52784 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-BoldItalic.ttf | bin | 57236 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-ExtraBold.ttf | bin | 52912 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-ExtraBoldItalic.ttf | bin | 57416 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-Italic.ttf | bin | 57076 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-Medium.ttf | bin | 52744 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-MediumItalic.ttf | bin | 57200 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-Regular.ttf | bin | 52652 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-SemiBold.ttf | bin | 52796 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-SemiBoldItalic.ttf | bin | 57308 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-Black.ttf | bin | 52804 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-BlackItalic.ttf | bin | 57308 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-Bold.ttf | bin | 52772 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-BoldItalic.ttf | bin | 57184 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-ExtraBold.ttf | bin | 52908 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-ExtraBoldItalic.ttf | bin | 57368 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-Italic.ttf | bin | 56976 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-Medium.ttf | bin | 52732 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-MediumItalic.ttf | bin | 57164 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-Regular.ttf | bin | 52608 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-SemiBold.ttf | bin | 52772 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-SemiBoldItalic.ttf | bin | 57240 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-Black.ttf | bin | 52764 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-BlackItalic.ttf | bin | 57256 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-Bold.ttf | bin | 52724 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-BoldItalic.ttf | bin | 57236 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-ExtraBold.ttf | bin | 52812 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-ExtraBoldItalic.ttf | bin | 57336 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-Italic.ttf | bin | 56980 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-Medium.ttf | bin | 52676 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-Regular.ttf | bin | 52584 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-SemiBold.ttf | bin | 52744 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-SemiBoldItalic.ttf | bin | 57240 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/fira_code/OFL.txt | 93 | ||||
-rw-r--r-- | themes/conspiracy/assets/fonts/fira_code/README.txt | 67 | ||||
-rw-r--r-- | themes/conspiracy/assets/fonts/fira_code/static/FiraCode-Bold.ttf | bin | 188876 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/fira_code/static/FiraCode-Light.ttf | bin | 188468 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/fira_code/static/FiraCode-Medium.ttf | bin | 188244 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/fira_code/static/FiraCode-Regular.ttf | bin | 188252 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/fira_code/static/FiraCode-SemiBold.ttf | bin | 188604 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/manuskript_gothisch/OFL-FAQ.txt | 242 | ||||
-rw-r--r-- | themes/conspiracy/assets/fonts/manuskript_gothisch/Open Font License.txt | 95 | ||||
-rw-r--r-- | themes/conspiracy/assets/fonts/manuskript_gothisch/download.txt | 2 | ||||
-rw-r--r-- | themes/conspiracy/assets/fonts/nyght-serif-main/COPYRIGHT.md | 1 | ||||
-rw-r--r-- | themes/conspiracy/assets/fonts/nyght-serif-main/HELLO.txt | 35 | ||||
-rw-r--r-- | themes/conspiracy/assets/fonts/nyght-serif-main/LICENSE.txt | 94 | ||||
-rw-r--r-- | themes/conspiracy/assets/fonts/nyght-serif-main/README.md | 32 | ||||
-rw-r--r-- | themes/conspiracy/assets/fonts/nyght-serif-main/TRADEMARKS.md | 1 | ||||
-rw-r--r-- | themes/conspiracy/assets/fonts/nyght-serif-main/fonts/.gitkeep | 0 | ||||
-rw-r--r-- | themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/.gitkeep | 0 | ||||
-rw-r--r-- | themes/conspiracy/assets/fonts/roboto_slab/LICENSE.txt | 202 | ||||
-rw-r--r-- | themes/conspiracy/assets/fonts/roboto_slab/README.txt | 71 | ||||
-rw-r--r-- | themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-Black.ttf | bin | 126876 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-Bold.ttf | bin | 126676 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-ExtraBold.ttf | bin | 126848 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-ExtraLight.ttf | bin | 126348 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-Light.ttf | bin | 126328 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-Medium.ttf | bin | 126624 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-Regular.ttf | bin | 125936 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-SemiBold.ttf | bin | 126840 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-Thin.ttf | bin | 125124 -> 0 bytes | |||
-rw-r--r-- | themes/conspiracy/layouts/404.html | 0 | ||||
-rw-r--r-- | themes/conspiracy/layouts/_default/baseof.html | 17 | ||||
-rw-r--r-- | themes/conspiracy/layouts/_default/list.html | 18 | ||||
-rw-r--r-- | themes/conspiracy/layouts/_default/single.html | 19 | ||||
-rw-r--r-- | themes/conspiracy/layouts/index.html | 25 | ||||
-rw-r--r-- | themes/conspiracy/layouts/partials/breadcrumbs.html | 7 | ||||
-rw-r--r-- | themes/conspiracy/layouts/partials/card.html | 17 | ||||
-rw-r--r-- | themes/conspiracy/layouts/partials/footer.html | 6 | ||||
-rw-r--r-- | themes/conspiracy/layouts/partials/head.html | 10 | ||||
-rw-r--r-- | themes/conspiracy/layouts/partials/header.html | 20 | ||||
-rw-r--r-- | themes/conspiracy/theme.toml | 21 |
336 files changed, 5129 insertions, 6021 deletions
diff --git a/.gitignore b/.gitignore deleted file mode 100644 index a48cf0d..0000000 --- a/.gitignore +++ /dev/null @@ -1 +0,0 @@ -public diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e69de29..0000000 --- a/.gitmodules +++ /dev/null diff --git a/about/index.html b/about/index.html new file mode 100644 index 0000000..cd5601a --- /dev/null +++ b/about/index.html @@ -0,0 +1,98 @@ +<!DOCTYPE html> +<html><head> + <meta charset="utf-8"> + <title>About jaseg | Home</title> + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="mobile-web-app-capable" content="yes"> + <meta name="color-scheme" content="dark light"> + <link rel="stylesheet" href="/style.css"> +</head> +<body><nav> + <div class="internal"> + + <a href="/" title="Home">Home</a> + <a href="/blog/" title="Blog">Blog</a> + <a href="/projects/" title="Projects">Projects</a> + <a href="/about/" title="About" class="active">About</a> + </div> + <div class="external"> + <a href="https://git.jaseg.de/" title="cgit">cgit</a> + <a href="https://github.com/jaseg" title="Github">Github</a> + <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a> + <a href="https://chaos.social/jaseg" title="Mastodon">Mastodon</a> + </span> +</nav> + + <header> + <h1>About jaseg</h1> +<ul class="breadcrumbs"> + <li><a href="/">jaseg.de</a></li><li><a href="/about/">About jaseg</a></li> +</ul> + + </header> + <main> + <div class="document"> + + +<div class="section" id="about"> +<h2>About</h2> +<p>Hej, I'm Jan, or jaseg. At the moment I'm doing a PhD (Dr.-Ing.) at TU Darmstadt in Computer Science, specializing on +Hardware Security. This is my personal website where I publish things that I find interesting.</p> +<p>I self-host my code at <a class="reference external" href="https://git.jaseg.de/">git.jaseg.de</a>, but I am also on <a class="reference external" href="https://github.com/jaseg">github</a> +and on <a class="reference external" href="https://gitlab.com/neinseg">gitlab</a>. I use github for issue tracking for some of my projects such as +<a class="reference external" href="https://github.com/jaseg/gerbolyze">gerbolyze</a> and <a class="reference external" href="https://github.com/jaseg/python-mpv">python-mpv</a>. I maintain +the <a class="reference external" href="https://pypi.org/project/python-mpv/">python-mpv</a> and <a class="reference external" href="https://pypi.org/project/gerbolyze/">gerbolyze</a> python +packages on PyPI. Release tags on these two repositories are signed with the release signing key found <a class="reference external" href="https://github.com/jaseg.gpg">on github</a> and below.</p> +<p>I am not on any social network, but feel free to write me an email at <a class="reference external" href="mailto:hello@jaseg.de?subject=About page on blog.jaseg.de">hello@jaseg.de</a>.</p> +<p>I do not use application-level email encryption such as S/MIME or PGP. If you need a higher level of secrecy than +regular old email provides, please ask around for my signal contact or email me a file encrypted using <a class="reference external" href="https://github.com/FiloSottile/age">age</a> with one of the SSH keys listed <a class="reference external" href="https://github.com/jaseg.keys">on my github</a>. You can find both PGP and other SSH keys that I have used in the past on the +internet, but please consider these keys revoked, and do not use them to encrypt anything you send me.</p> +<div class="section" id="python-package-release-signing-key"> +<h3>Python package release signing key</h3> +<p>I use this GPG key (key ID <tt class="docutils literal">ED7A208EEEC76F2D</tt>) to sign git release tags of both <a class="reference external" href="https://github.com/jaseg/gerbolyze">gerbolyze</a> and <a class="reference external" href="https://github.com/jaseg/python-mpv">python-mpv</a>:</p> +<pre class="literal-block"> +<span class="lineno"></span><span class="line">-----BEGIN PGP PUBLIC KEY BLOCK-----</span> +<span class="lineno"></span><span class="line">mDMEXom49xYJKwYBBAHaRw8BAQdA/KrWMt2MKGIZUvlQZnWjNd6i8/ZYjRsBQqEf</span> +<span class="lineno"></span><span class="line">PJ8pJ+20NHB5dGhvbi1tcHYgUmVsZWFzZSBTaWduaW5nIEtleSA8cHl0aG9uLW1w</span> +<span class="lineno"></span><span class="line">dkBqYXNlZy5kZT6IlgQTFggAPhYhBONvdTB/Cg7C0UX/XO16II7ux28tBQJeibj3</span> +<span class="lineno"></span><span class="line">AhsDBQkSzAMABQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEO16II7ux28thRYA</span> +<span class="lineno"></span><span class="line">/3Yl1RdeUGor6K0RTxce9TIBB+DpLNupJgB9f6onuocpAQC614zQ/RQ6rkGTHCwA</span> +<span class="lineno"></span><span class="line">ElFClWRQ5eppj0jpAuH15udqAbg4BF6JuPcSCisGAQQBl1UBBQEBB0A0mrXSv6rj</span> +<span class="lineno"></span><span class="line">ajCmZR4H4OtowAx477YS+yWARqo1NtdgJwMBCAeIfgQYFggAJhYhBONvdTB/Cg7C</span> +<span class="lineno"></span><span class="line">0UX/XO16II7ux28tBQJeibj3AhsMBQkSzAMAAAoJEO16II7ux28tMZwBAIUpHHvP</span> +<span class="lineno"></span><span class="line">gRW2jQuzdw1r06kItfFk/0t+mgNUQ2+vtbhzAP98BoWx7lv+bvlIbBaVgLldusj0</span> +<span class="lineno"></span><span class="line">pHnZI/0y3ksMBkdbBw==</span> +<span class="lineno"></span><span class="line">=Mr6G</span> +<span class="lineno"></span><span class="line">-----END PGP PUBLIC KEY BLOCK----- +</span></pre> +</div> +</div> +<div class="section" id="about-this-site"> +<h2>About this site</h2> +<p>This site is made with the hugo static site generator. I made the theme myself, feel free to grab a copy at +<a class="reference external" href="https://git.jaseg.de/blog.git/tree/themes/conspiracy?h=main">git.jaseg.de</a>. The nifty auto-reflowing code embeds are +made with some CSS magic I made that you can find in <a class="reference external" href="https://git.jaseg.de/blog.git/tree/themes/conspiracy/assets/css/style.css?h=main&id=2fd22e30ce176d8d8a641fd371ad1623b082eaaf#n367">style.css</a>. +The body text is typeset in Roboto Slab, created by <a class="reference external" href="https://christianrobertson.com/">Christian Robertson</a> while +working at Google. The headlines are set in Nyght Serif, a font by <a class="reference external" href="https://linktr.ee/mkobuzan">Maksym Kobuzan</a>. +Check out their other fonts, their work is beautiful! Source code is typeset in Fira Code, a derivate by ... from +Mozilla's <a class="reference external" href="https://github.com/mozilla/Fira">Fira Mono</a> font, designed by <a class="reference external" href="https://spiekermann.com/">Erik Spiekermann</a>, <a class="reference external" href="https://carrois.com/">Ralph du Carrois</a>, <a class="reference external" href="https://anjameiners.com/de/hallo/">Anja Meiners</a> and Botio Nikoltchev of Carrois Type Design, now succeeded by <a class="reference external" href="https://bboxtype.com/typefaces/FiraMono/#!layout=specimen">bBoxType</a> , and Patryk Adamczyk of Mozilla. The photo of mountains +that's used in the background of this site is by <a class="reference external" href="https://www.conti.photos/">Fabrizio Conti</a> and can be found on +<a class="reference external" href="https://unsplash.com/photos/TUmjK7ZJgbI">Unsplash</a>.</p> +</div> +</div> + </main><footer> + Copyright © 2023 Jan Sebastian Götte + / <a href="/about/">About</a> + / <a href="/imprint/">Imprint</a> +</footer> +<script> + if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser + contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to + prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition. + In support of an open web, this website does not function with this DRM. Please install a browser such + as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports + ad blockers.`; + </script> + </body> +</html> diff --git a/archetypes/default.md b/archetypes/default.md deleted file mode 100644 index 00e77bd..0000000 --- a/archetypes/default.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: "{{ replace .Name "-" " " | title }}" -date: {{ .Date }} -draft: true ---- - diff --git a/blog/hsm-basics/index.html b/blog/hsm-basics/index.html new file mode 100644 index 0000000..904e471 --- /dev/null +++ b/blog/hsm-basics/index.html @@ -0,0 +1,240 @@ +<!DOCTYPE html> +<html><head> + <meta charset="utf-8"> + <title>Hardware Security Module Basics | Home</title> + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="mobile-web-app-capable" content="yes"> + <meta name="color-scheme" content="dark light"> + <link rel="stylesheet" href="/style.css"> +</head> +<body><nav> + <div class="internal"> + + <a href="/" title="Home">Home</a> + <a href="/blog/" title="Blog">Blog</a> + <a href="/projects/" title="Projects">Projects</a> + <a href="/about/" title="About">About</a> + </div> + <div class="external"> + <a href="https://git.jaseg.de/" title="cgit">cgit</a> + <a href="https://github.com/jaseg" title="Github">Github</a> + <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a> + <a href="https://chaos.social/jaseg" title="Mastodon">Mastodon</a> + </span> +</nav> + + <header> + <h1>Hardware Security Module Basics</h1> +<ul class="breadcrumbs"> + <li><a href="/">jaseg.de</a></li> + <li><a href="/blog/">Blog</a></li><li><a href="/blog/hsm-basics/">Hardware Security Module Basics</a></li> +</ul> + <strong>2019-05-17</strong> + </header> + <main> + <div class="document"> + + +<div class="section" id="hardware-security-modules-and-security-research-and-cryptography"> +<h2>Hardware Security Modules and Security Research and Cryptography</h2> +<p>On May 17 2019 I gave a short presentation on the fundamentals of hardware security modules at the weekly seminar of +Prof. Mori's security research working group at Waseda University. The motivation for this was that outside of low-level +hardware security people and people working in the financial industry HSMs are not thought about that often. In +particular most network or systems security people would not consider them an option. Also it could turn out to be +really interesting to think about what could be done with an HSM in conjunction with modern cryptography (instead of +just plain old RSA-OAEP and AES-CBC).</p> +<p><a class="reference external" href="mori_semi_hsm_talk_web.pdf">Click here to download a PDF with the slides for this talk.</a></p> +</div> +<div class="section" id="ideas-for-research-in-hsms"> +<h2>Ideas for research in HSMs</h2> +<p>Preparing for this talk brought me back to some research ideas I've been working on for a while now. Since I'm not sure +I'll find the time to properly research this topic, I thought it would be great to write down some rought outlines first +for future reference.</p> +<div class="section" id="the-problem-with-current-hsm-tech"> +<h3>The Problem with current HSM tech</h3> +<p>Currently, HSMs are only used in certain specific niche applications such as certificate authority key management and +financial transaction data handling. One key reason for this is that HSMs currently don't provide the affordances that +would be needed for them to be adopted more widely by the cryptographic and security engineering community. As far as I +can tell, the two core missing affordances are:</p> +<ol class="arabic simple"> +<li>To be more widely adopted, HSMs must become less expensive. Currently, they go for several tens of thousands of Euro, +which puts them outside most budgets.</li> +<li>To be more widely adopted, HSMs must provide the standardized programming interfaces familiar to cryptographic +developers. Currently, every HSM vendor has their own custom cryptographic API and a developer will have to train on +one specific vendor's tooling. Furthermore, any documentation of these internals is kept secret behind NDAs. This +constitutes a high barrier to entry, decreasing adoption in particular with young developers accustomed to +open-source ecosystems.</li> +</ol> +</div> +<div class="section" id="attacking-cost-of-implementation"> +<h3>Attacking cost of implementation</h3> +<p>The first issue can be addressed by simply creating a viable low-cost alternative. There is no fundamental technical +reason for the high cost of HSMs. This cost is instead due to manufacturers trying to recoup their expenses for R&D as +well as certification from the small volumes HSMs are sold in.</p> +<p>Compared to system integration and certification the pure R&D cost of HSM defense mechanisms themselves is not too high +in an academic context it should be feasible to develop a sort of HSM blueprint that can then be cheaply produced by +anyone in need. Since the application areas outlined here are far from the core business areas of the clients of +established HSM vendors this would most likely not be a realistic threat to any established vendor's business and a +co-existence of both should not pose any problems in the short term.</p> +</div> +<div class="section" id="benefits-of-an-academic-hsm-standard"> +<h3>Benefits of an academic HSM standard</h3> +<p>Tackling the high cost of current HSM hardware with an open-source HSM blueprint would yield +several academic advantages beyond cost reduction.</p> +<ol class="arabic simple"> +<li>An open-source blueprint could serve as an academic reference design to evaluate and compare other HSM designs +against. For instance this would not only allow quantifying the effectiveness of academic security measures but also +allow an evaluation of commercial HSMs.</li> +<li>An open-source blueprint could stimulate academic research in this academically very quiet albeit commercially +important area. This research would ultimately benefit everyone employing HSMs by raising security standards in the +field. Since HSMs are never solely relied upon for overal system security both defensive and offensive security +research would yield these benefits.</li> +<li>An open-source blueprint would encourage new people to get into the field and both apply HSMs to practical problems +as well as improve HSMs themselves. Currently, this is highly discouraged due to the strictly proprietary nature of +all available systems.</li> +<li>Finally, developing an open-source HSM blueprint might yield new findings in adjacent academic areas due to the +hightly multi-disciplinary nature of security research in general and HSM design in particular.</li> +</ol> +</div> +<div class="section" id="scope-of-an-academic-hsm-standard"> +<h3>Scope of an academic HSM standard</h3> +<p>An academic HSM blueprint would need to be flexible so that researchers can adapt it to their particular problem. A +modular architecture would lend itself to this flexibility. Fundamentally, there would be three components to this +architecture. First, a <strong>base</strong> containing infrastructure such as the surveillance microcontroller, power supplies, +power supply filtering and hardware DPA countermeasures, and possibly a standardized mechanical and electrical +interface.</p> +<p>Next to the base, a system integrator would put their <em>payload</em>. The nature of this payload is intentionally kept +unspecified, and it might be anything from a cryptographic microcontroller to a small embedded system such as a +raspberry pi single board computer. Keeping the <em>payload</em> open like this achieves two benefits: It gives the HSM +blueprint's user <em>their</em> familiar tooling and the hardware <em>they</em> need, allowing fast adoption. Someone well-versed in +e.g. Javascript could literally implement their cryptography in Javascript, run it on an off-the-shelf raspberry pi and +just apply the HSM blueprint around it. In addition, keeping the <em>payload</em> open reduces the scope of what needs to be +implemented. Building a general SDK on top of something like a bare ARM SoC such as a TI OMAP or a Freescale/NXP IMX +would be a considerable additional burden, on top of the actual HSM design. Keeping the <em>payload</em> open allows research +to concentrate on the actual point, the HSM design.</p> +<p>The final and most important component would be a set of <em>security measures</em> that can be combined with the base to +form the final HSM. Each of these <em>security measures</em> would entail a detailed specification of its design, manufacture +and security properties. These <em>security measures</em> could be simple things like tamper switches or potting, but could +also be complex things like security meshes.</p> +<p>Given these three components -- <em>base</em>, <em>payload</em> and <em>security measures</em> as detailed specifications any engineer should +be able to design and manufacture a HSM customized to their needs. Unifying these three components within the HSM +blueprint would be a set of reference designs. Each reference design would implement a particular parametrization of the +three architectural components with a physical hole cut out where the payload would go.. These reference designs would +for one serve to guide any implementer on the customization and integration of their own derivation from the blueprint. +In addition it would serve as an extremely simple, low-cost point of entry into the ecosystem. A curious researcher +could simply replicate the reference design and put their existing payload inside. Practically this might mean e.g. a +researcher having PCBs produced according to the design files for a reference design for a mesh-based HSM, producing +their own mesh, physically glueing a raspberry pi SBC into the middle of it, and potting the resulting system. Given the +ease of prototype PCB fabrication today this would realistically allow evaluation of HSM technologies on a budget that +is orders of magnitude less than the cost of current HSMs.</p> +</div> +</div> +<div class="section" id="research-ideas-for-tamper-detection-mechanisms"> +<h2>Research ideas for tamper detection mechanisms</h2> +<p>The core component of an HSM blueprint would be a suite of tamper detection mechanisms. Following are a few ideas on how +to improve on the current state of the art of membrane tamper switches plus temperature sensors plus PCB and printed +security meshes plus potting.</p> +<div class="section" id="diy-or-small-lab-mesh-production"> +<h3>DIY or small lab mesh production</h3> +<p><strong>Analog sensing</strong> meshes are a proven technology where instead of just monitoring for continuity and shorts, analog +parameters of the mesh traces such as inductance and mutual capacitance are monitored. In 2019, <a class="reference external" href="https://tches.iacr.org/index.php/TCHES/article/view/7334">Immler et al. published +a paper</a> where took this principle and turned it all the +way up. They directly derived a cryptographic secret from the analog properties of their HSM's security mesh in an +attempt to built a <a class="reference external" href="https://en.wikipedia.org/wiki/Physical_unclonable_function">Physically Unclonable Function, or PUF</a>. The idea with PUFs is that they reproduce some entropy +that comes from random tolerances of their production process. The same PUF will always yield (approximately) the same +key, but since you cannot control these random production variations, in practice the resulting PUF cannot be cloned. +Note however, that its secrets can of course be copied if you find a way to read them out.</p> +<p>As Immler et al. demonstrated in their paper, you don't need any secret sauce to create an analog mesh sensing circuit. +All you need are a bunch of (admittedly, expensive) off-the-shelf analog ICs. The interesting bit here is that by +applying more advanced analog sensing, weaknesses of an otherwise coarse mesh desing could maybe be alleviated. That is, +instead of monitoring a very fine mesh for continuity, you could instead closely monitor inductance and capacitance of a +more coarse mesh. This trade-off between sensing circuit complexity (resp. cost) and mesh production capabilities may +allow someone with a poorly equipped lab to still make a decent HSM. The question is, how do you produce a "decent" mesh +given only basic tools? Here are some ideas.</p> +<p><strong>3D metal patterning techniques</strong> refers to any technique for producing thin, patterned metal structures on a +three-dimensional plastic substrate. The basic process would consist of 3D-printing the polymer substrate, depositing a +thin metal layer on top and then patterning this metal layer. A good starting point here would be the recent work of +<a class="reference external" href="https://www.youtube.com/watch?v=Z228xymQYho">Ben Kraznow</a> on this exact thing.</p> +<p><strong>Copper filament methods</strong> would be any method embedding copper wire from a spool in some resin or other matrix. This +could mean either of a systematic approach of carefully winding or folding the copper wire into patterns or a +non-systematic approach of simply stuffing a large tangle of copper wire into a small space. The main challenge with the +former would be to find a non-tedious way of production. The main challenge with the latter would be to find process +parameters that guarantee complete coverage of the HSM without holes or other areas of lower sensitivity to intrusions. +Both approaches would require careful consideration of the overall design including the polymer resin supporting +structure to ensure sensitivity against attacks since copper wire is mechanically much stronger than the micrometre-thin +metal coatings used in patterning techniques.</p> +</div> +<div class="section" id="envelope-measurement"> +<h3>Envelope measurement</h3> +<p>Finally, I think there is another set of currently under-utilized tamper-detection methods that would be very +interesting to explore. I am not aware of an academic term for these, so I am just going to dub them <em>envelope +measurement</em> here.</p> +<p>The fundamental apporach of a mesh is to build a physical security envelope (the mesh) that physically detects when it +is disturbed (open or short circuits). This approach works well but has the disadvantage that these meshes are rather +complex to manufacture since effectively every part of them is acting as a sensing element. A conceptually more complex +but in practice potentially simpler approach might be to split the functions of security envelope and sensing element. +This would mean that in place of the mesh, some form of passive element such as metal foil forms the security envelope +which is then checked for tampering using a very sensitive sensor inside. This remote-sensing approach might simplify +the manufacture of the envelope itself and thus yield a design that is more easily customized. Following are a few ideas +on how to approach this envelope measurement problem.</p> +<p><strong>Ultrasonic</strong> If the HSM is potted, a few ultrasonic transducers could be added inside the potting. With several +transducers, any one could be used to transmit ultrasound while the others measure complex phase and energy of the +signal they receive. The circuitry for this could be made fairly simple if using a static transmit frequency or a low +chirp rate by using a homodyne receiver built around a comparator fed into some timers. This approach would likely +detect any mechanical attack and would also rule out chemical attacks involving liquids (though starting from which +amount of liquid depends on receiver sensitivity). The main disadvantages might be high power consumption and cost and +size of the ultrasonic transducers. Traditional cheap transducers made for air as a transmission medium are fairly large +and might not adequately couple into potting compound. If somehow one could convince a standard small piezo element to +do the same job that would be great as far as cost and size are concerned. A concern in some fringe use cases might be +suceptibility to ambient noise, though this could easily be reduced at the expense of space and heat dissipation +capacity by adding sound dampening on the outside. A likely attack vector against this approach might be using a laser +cutter to drill a hole through the potting compound, then inserting probes carefully chosen to not couple too much +to the potting compound ultrasonically.</p> +<p><strong>Light</strong> In either an unpotted HSM or one potted with a transparent (at some wavelengths) potting compound one could +embed LEDs and photodiodes in a similar setup to the ultrasonic setup described above. In contrast to the ultrasound, +the LEDs would literally have to light up the HSM's interior and shadows might be an issue since the HSM is likely some +flat rectangular shape. A possible solution to this would be to coat both the embedded payload and the lid with some +highly reflective paint such as some glossy silver paint or simple white paint. The basic approach might be as simple as +simply turning on several LEDs distributed throughout the HSM in turn and measuring amplitude at several photodetectors, +or as complex as doing a LIDAR-like phase measurement sweeping through a range of frequencies to determine not only +absorption but also phase/distance characteristics between emitter LED and detector photodiode. Using some high-gain TIA +along with a homodyne detector (lock-in amplifier) and changing emitter intensity, very precise measurements of both +absorption and phase might be possible, as might be measurements through almost opaque, diffuse potting compounds such +as a grey epoxide resin. The main disadvantages of this method would likely be the need to thoroughly light-proof the +entire HSM (likely by wrapping it in metal foil) and the potentially high cost of transmitter and receiver circuitry +(nice TIAs aren't cheap). To be effective against attacks using e.g. very fine drills and probes the system would likely +have to be very sensitive.</p> +<p><strong>Radar</strong> Finally, one could turn to standard radar techniques to fingerprint the inside of the HSM. The goal here would +be fingerprinting instead of mapping since only changes need to be detected. In this approach one could use homodyne +detection to improve sensitivity and reduce receiver complexity, and sweep frequencies similar to an FMCW radar (but +probably without exploiting the self-demodulation effect). Besides high cost, this approach has two disadvantages. +First, such a system would likely not go beyond 24GHz or maybe 40GHz due to component availability issues. Even at 40GHz +the wavelength in the potting compound would be in the order of magnitude of several millimeters. Fine intrusions using +some tool chosen to not interact too much with the EM field inside the HSM such as a heated ceramic needle or simply a +laser cutter might not be detectable using this approach. In any case, this system would certainly not be able to detect +small holes piercing the HSM enclosure. The HSM enclosure would have to be made into an RF shield, likely by using some +metal foil in it.</p> +<p>Overall in the author's opinion these three techniques are most promising in order <em>Light</em>, <em>Ultrasonic</em>, <em>Radar</em>. Light +would prbably provide the best sensitivity at expense of some cost. Ultrasonic might be used in conjunction with light +to cover some additional angles since it is potentially very low-cost. Radar seems hard to engineer into a solution that +works reliably and also would likely be at least an order of magnitude more expensive than the other two technologies +while not providing better sensitivity.</p> +</div> +</div> +</div> + </main><footer> + Copyright © 2023 Jan Sebastian Götte + / <a href="/about/">About</a> + / <a href="/imprint/">Imprint</a> +</footer> +<script> + if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser + contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to + prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition. + In support of an open web, this website does not function with this DRM. Please install a browser such + as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports + ad blockers.`; + </script> + </body> +</html> diff --git a/content/blog/hsm-basics/mori_semi_hsm_talk_web.pdf b/blog/hsm-basics/mori_semi_hsm_talk_web.pdf Binary files differindex b8b7177..b8b7177 100644 --- a/content/blog/hsm-basics/mori_semi_hsm_talk_web.pdf +++ b/blog/hsm-basics/mori_semi_hsm_talk_web.pdf diff --git a/blog/ihsm-worlds-first-diy-hsm/index.html b/blog/ihsm-worlds-first-diy-hsm/index.html new file mode 100644 index 0000000..bbb00ca --- /dev/null +++ b/blog/ihsm-worlds-first-diy-hsm/index.html @@ -0,0 +1,81 @@ +<!DOCTYPE html> +<html><head> + <meta charset="utf-8"> + <title>New Paper on Inertial Hardware Security Modules | Home</title> + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="mobile-web-app-capable" content="yes"> + <meta name="color-scheme" content="dark light"> + <link rel="stylesheet" href="/style.css"> +</head> +<body><nav> + <div class="internal"> + + <a href="/" title="Home">Home</a> + <a href="/blog/" title="Blog">Blog</a> + <a href="/projects/" title="Projects">Projects</a> + <a href="/about/" title="About">About</a> + </div> + <div class="external"> + <a href="https://git.jaseg.de/" title="cgit">cgit</a> + <a href="https://github.com/jaseg" title="Github">Github</a> + <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a> + <a href="https://chaos.social/jaseg" title="Mastodon">Mastodon</a> + </span> +</nav> + + <header> + <h1>New Paper on Inertial Hardware Security Modules</h1> +<ul class="breadcrumbs"> + <li><a href="/">jaseg.de</a></li> + <li><a href="/blog/">Blog</a></li><li><a href="/blog/ihsm-worlds-first-diy-hsm/">New Paper on Inertial Hardware Security Modules</a></li> +</ul> + <strong>2021-11-23</strong> + </header> + <main> + <div class="document" id="world-s-first-diy-hsm"> +<h1 class="title">World's First DIY HSM</h1> + +<p>Last week, Prof. Dr. Björn Scheuermann and I have <a class="reference external" href="https://tches.iacr.org/index.php/TCHES/article/view/9290">published our first joint paper on Hardware Security Modules</a>. In our paper, we introduce Inertial Hardware Security +Modules (IHSMs), a new way of building high-security HSMs from basic components. I think the technology we demonstrate +in our paper might allow some neat applications where some civil organization deploys a service that no one, not even +they themselves, can snoop on. Anyone can built an IHSM without needing any fancy equipment, which makes me optimistic +that maybe the ideas of the <a class="reference external" href="https://www.activism.net/cypherpunk/manifesto.html">Cypherpunk movement</a> aren't obsolete +after all, despite even the word "crypto" having been co-opted by radical capitalist environmental destructionists.</p> +<p>An IHSM is basically an ultra-secure enclosure for something like a server or a raspberry pi that even someone with +unlimited resources would have a really hard time cracking without destroying all data stored in it. The principle of an +IHSM is the same as that of a <a class="reference external" href="http://jaseg.de/blog/hsm-basics/">normal HSM</a>. You have a payload that contains really secret data. There's really no way +to prevent an attacker with physical access to the thing from opening it given enough time and abrasive discs for their +angle grinder. So what you do instead is that you make it self-destruct its secrets within microseconds of anyone +tampering with it. Usually, such HSMs are used for storing credit card pins and other financial data. They're expensive +as fuck, all the while being about the same processing speed as a smartphone. Traditional HSMs use printed or +lithographically patterned conductive foils for their security mesh. These foils are not an off-the-shelf component and +are made in a completely custom manufacturing process. To create your own, you would have to re-engineer that entire +process and probably spend some serious money on production machines.</p> +<p>Inertial HSMs take the concept of traditional HSMs, but replace the usual tamper detection mesh with a few security mesh +PCBs. These PCBs are coarser than traditional meshes by orders of magnitude, and would alone not even be close to enough +to keep out even a moderately motivated attacker. IHSMs fix this issue by spinning the entire tamper detection mesh at +very high speed. To tamper with the mesh, an attacker would have to stop it. This, in turn, can be easily detected by +the mesh's alarm circuitry using a simple accelerometer as a rotation sensor.</p> +<p>In our paper, we have shown a working prototype of the core concepts one needs to build such an IHSM. To build an IHSM +you only need a basic electronics lab. I built the prototype in our paper at home during one of Germany's COVID +lockdowns. You can have a look at our code and CAD on <a class="reference external" href="https://git.jaseg.de/ihsm.git">my git</a>. What is missing right +now is an integration of all of these fragments into something cohesive that an interested person with the right tools +could go out and build. We are planning to release this sort of documentation at some point, but right now we are +focusing our effort on the next iteration of the design instead. Stay tuned for updates ;)</p> +</div> + </main><footer> + Copyright © 2023 Jan Sebastian Götte + / <a href="/about/">About</a> + / <a href="/imprint/">Imprint</a> +</footer> +<script> + if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser + contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to + prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition. + In support of an open web, this website does not function with this DRM. Please install a browser such + as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports + ad blockers.`; + </script> + </body> +</html> diff --git a/blog/index.html b/blog/index.html new file mode 100644 index 0000000..ed7f2b8 --- /dev/null +++ b/blog/index.html @@ -0,0 +1,139 @@ +<!DOCTYPE html> +<html><head> + <meta charset="utf-8"> + <title>Blog | Home</title> + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="mobile-web-app-capable" content="yes"> + <meta name="color-scheme" content="dark light"> + <link rel="stylesheet" href="/style.css"> +</head> +<body><nav> + <div class="internal"> + + <a href="/" title="Home">Home</a> + <a href="/blog/" title="Blog" class="active">Blog</a> + <a href="/projects/" title="Projects">Projects</a> + <a href="/about/" title="About">About</a> + </div> + <div class="external"> + <a href="https://git.jaseg.de/" title="cgit">cgit</a> + <a href="https://github.com/jaseg" title="Github">Github</a> + <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a> + <a href="https://chaos.social/jaseg" title="Mastodon">Mastodon</a> + </span> +</nav> + + <header> + <h1>Blog</h1> +<ul class="breadcrumbs"> + <li><a href="/">jaseg.de</a></li><li><a href="/blog/">Blog</a></li> +</ul> + + </header> + <main class="cards"> + <div class="intro"> + <div class="document"> + + + +</div> + </div> + <div class="card"><h3><a href="/blog/telekom-gpon-sfp/">Ubiquiti EdgeRouter on Deutsche Telekom GPON Fiber</a></h3><strong>2022-02-21</strong> + + <div class="summary"> + Disclaimer I provide this guide as a reference for other knowledgeable users without any warranty. Please feel free to use this as a resource but do not hold me responsible if this does not work for you. There is a significant chance that due to an error on my side or due to Telekom changing their setup this guide will not work for you, and you may end up having to pay for an unsuccessful Telekom technician visit. + <a href="http://jaseg.de/blog/telekom-gpon-sfp/">Read more</a> + </div> +</div> + + <div class="card"><h3><a href="/blog/ihsm-worlds-first-diy-hsm/">New Paper on Inertial Hardware Security Modules</a></h3><strong>2021-11-23</strong> + + <div class="summary"> + World's First DIY HSM Last week, Prof. Dr. Björn Scheuermann and I have published our first joint paper on Hardware Security Modules. In our paper, we introduce Inertial Hardware Security Modules (IHSMs), a new way of building high-security HSMs from basic components. I think the technology we demonstrate in our paper might allow some neat applications where some civil organization deploys a service that no one, not even they themselves, can snoop on. + <a href="http://jaseg.de/blog/ihsm-worlds-first-diy-hsm/">Read more</a> + </div> +</div> + + <div class="card"><h3><a href="/blog/kicad-mesh-plugin/">Kicad Mesh Plugin</a></h3><strong>2020-08-18</strong> + + <div class="summary"> + Tamper Detection Meshes Cryptography is at the foundation of our modern, networked world. From email to card payment infrastructure in brick and mortar stores, cryptographic keys secure almost every part of our digital lives againts cybercriminals or curious surveillance capitalists. Without cryptography, many of the things we routinely do in our lives such as paying for groceries with a credit card, messaging a friend on Signal or unlocking a car with its keyfob would not be possible. + <a href="http://jaseg.de/blog/kicad-mesh-plugin/">Read more</a> + </div> +</div> + + <div class="card"><h3><a href="/blog/private-contact-discovery/">Private Contact Discovery</a></h3><strong>2019-06-22</strong> + + <div class="summary"> + Private Contact Discovery Private Contact Discovery (PCD) is the formal name for the problem modern smartphone messenger applications have on installation: Given a user's address book, find out which of their contacts also use the same messenger without the messenger's servers learning anything about the user's address book. The widespread non-private way to do this is to simply upload the user's address book to the app's operator's servers and do an SQL JOIN keyed on the phone number field against the database of registered users. + <a href="http://jaseg.de/blog/private-contact-discovery/">Read more</a> + </div> +</div> + + <div class="card"><h3><a href="/blog/hsm-basics/">Hardware Security Module Basics</a></h3><strong>2019-05-17</strong> + + <div class="summary"> + Hardware Security Modules and Security Research and Cryptography On May 17 2019 I gave a short presentation on the fundamentals of hardware security modules at the weekly seminar of Prof. Mori's security research working group at Waseda University. The motivation for this was that outside of low-level hardware security people and people working in the financial industry HSMs are not thought about that often. In particular most network or systems security people would not consider them an option. + <a href="http://jaseg.de/blog/hsm-basics/">Read more</a> + </div> +</div> + + <div class="card"><h3><a href="/blog/serial-protocols/">How to talk to your microcontroller over serial</a></h3><strong>2018-05-19</strong> + + <div class="summary"> + Scroll to the end for the TL;DR. +In this article I will give an overview on the protocols spoken on serial ports, highlighting common pitfalls. I will summarize some points on how to design a serial protocol that is simple to implement and works reliably even under error conditions. +If you have done low-level microcontroller firmware you will regularly have had to stuff some data up a serial port to another microcontroller or to a computer. + <a href="http://jaseg.de/blog/serial-protocols/">Read more</a> + </div> +</div> + + <div class="card"><h3><a href="/blog/thors-hammer/">Thor's Hammer</a></h3><strong>2018-05-03</strong> + + <div class="summary"> + In case you were having an inferiority complex because your friends' IBM Model M keyboards are so much louder than the shitty rubber dome freebie you got with your pc... Here's the solution: Thor's Hammer, a simple typing cadence enhancer for PS/2 keyboards. +Your browser does not support the HTML5 video tag. A demonstration of the completed project. h264 download / webm download The connects to the keyboard's PS/2 clock line and briefly actuates a large solenoid on each key press. + <a href="http://jaseg.de/blog/thors-hammer/">Read more</a> + </div> +</div> + + <div class="card"><h3><a href="/blog/multichannel-led-driver/">32-Channel LED tape driver</a></h3><strong>2018-05-02</strong> + + <div class="summary"> + Theoretical basics Together, a friend and I outfitted the small staircase at Berlin's Chaos Computer Club with nice, shiny RGB-WW LED tape for ambient lighting. This tape is like regular RGB tape but with an additional warm white channel, which makes for much more natural pastels and whites. There are several variants of RGBW tape. Cheap ones have separate RGB and white LEDs, which is fine for indirect lighting but does not work for direct lighting. + <a href="http://jaseg.de/blog/multichannel-led-driver/">Read more</a> + </div> +</div> + + <div class="card"><h3><a href="/blog/wifi-led-driver/">Wifi Led Driver</a></h3><strong>2018-05-02</strong> + + <div class="summary"> + Project motivation The completed driver board installed in the 3D-printed case. This device can now be connected to 12V and two segments of LED tape that can then be controlled trough Wifi. The ESP8266 module goes on the pin header on the left and was removed for this picture. After the multichannel LED driver was completed, I was just getting used to controlling LEDs at 14-bit resolution. I liked the board we designed in this project, but at 32 channels it was a bit large for most use cases. + <a href="http://jaseg.de/blog/wifi-led-driver/">Read more</a> + </div> +</div> + + <div class="card"><h3><a href="/blog/led-characterization/">LED Characterization</a></h3><strong>2018-05-02</strong> + + <div class="summary"> + Preface Recently, I have been working on a small driver for ambient lighting using 12V LED strips like you can get inexpensively from China. I wanted to be able to just throw one of these somewhere, stick down some LED tape, hook it up to a small transformer and be able to control it through Wifi. When I was writing the firmware, I noticed that when fading between different colors, the colors look all wrong! + <a href="http://jaseg.de/blog/led-characterization/">Read more</a> + </div> +</div> + + </main><footer> + Copyright © 2023 Jan Sebastian Götte + / <a href="/about/">About</a> + / <a href="/imprint/">Imprint</a> +</footer> +<script> + if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser + contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to + prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition. + In support of an open web, this website does not function with this DRM. Please install a browser such + as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports + ad blockers.`; + </script> + </body> +</html> diff --git a/blog/index.xml b/blog/index.xml new file mode 100644 index 0000000..0d40360 --- /dev/null +++ b/blog/index.xml @@ -0,0 +1,105 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> + <channel> + <title>Blog on Home</title> + <link>http://jaseg.de/blog/</link> + <description>Recent content in Blog on Home</description> + <generator>Hugo -- gohugo.io</generator> + <language>en-us</language> + <copyright>Jan Sebastian Götte</copyright> + <lastBuildDate>Mon, 21 Feb 2022 20:00:00 +0100</lastBuildDate><atom:link href="http://jaseg.de/blog/index.xml" rel="self" type="application/rss+xml" /> + <item> + <title>Ubiquiti EdgeRouter on Deutsche Telekom GPON Fiber</title> + <link>http://jaseg.de/blog/telekom-gpon-sfp/</link> + <pubDate>Mon, 21 Feb 2022 20:00:00 +0100</pubDate> + + <guid>http://jaseg.de/blog/telekom-gpon-sfp/</guid> + <description>Disclaimer I provide this guide as a reference for other knowledgeable users without any warranty. Please feel free to use this as a resource but do not hold me responsible if this does not work for you. There is a significant chance that due to an error on my side or due to Telekom changing their setup this guide will not work for you, and you may end up having to pay for an unsuccessful Telekom technician visit.</description> + </item> + + <item> + <title>New Paper on Inertial Hardware Security Modules</title> + <link>http://jaseg.de/blog/ihsm-worlds-first-diy-hsm/</link> + <pubDate>Tue, 23 Nov 2021 23:42:20 +0100</pubDate> + + <guid>http://jaseg.de/blog/ihsm-worlds-first-diy-hsm/</guid> + <description>World's First DIY HSM Last week, Prof. Dr. Björn Scheuermann and I have published our first joint paper on Hardware Security Modules. In our paper, we introduce Inertial Hardware Security Modules (IHSMs), a new way of building high-security HSMs from basic components. I think the technology we demonstrate in our paper might allow some neat applications where some civil organization deploys a service that no one, not even they themselves, can snoop on.</description> + </item> + + <item> + <title>Kicad Mesh Plugin</title> + <link>http://jaseg.de/blog/kicad-mesh-plugin/</link> + <pubDate>Tue, 18 Aug 2020 13:15:39 +0200</pubDate> + + <guid>http://jaseg.de/blog/kicad-mesh-plugin/</guid> + <description>Tamper Detection Meshes Cryptography is at the foundation of our modern, networked world. From email to card payment infrastructure in brick and mortar stores, cryptographic keys secure almost every part of our digital lives againts cybercriminals or curious surveillance capitalists. Without cryptography, many of the things we routinely do in our lives such as paying for groceries with a credit card, messaging a friend on Signal or unlocking a car with its keyfob would not be possible.</description> + </item> + + <item> + <title>Private Contact Discovery</title> + <link>http://jaseg.de/blog/private-contact-discovery/</link> + <pubDate>Sat, 22 Jun 2019 10:30:00 +0800</pubDate> + + <guid>http://jaseg.de/blog/private-contact-discovery/</guid> + <description>Private Contact Discovery Private Contact Discovery (PCD) is the formal name for the problem modern smartphone messenger applications have on installation: Given a user's address book, find out which of their contacts also use the same messenger without the messenger's servers learning anything about the user's address book. The widespread non-private way to do this is to simply upload the user's address book to the app's operator's servers and do an SQL JOIN keyed on the phone number field against the database of registered users.</description> + </item> + + <item> + <title>Hardware Security Module Basics</title> + <link>http://jaseg.de/blog/hsm-basics/</link> + <pubDate>Fri, 17 May 2019 15:29:20 +0800</pubDate> + + <guid>http://jaseg.de/blog/hsm-basics/</guid> + <description>Hardware Security Modules and Security Research and Cryptography On May 17 2019 I gave a short presentation on the fundamentals of hardware security modules at the weekly seminar of Prof. Mori's security research working group at Waseda University. The motivation for this was that outside of low-level hardware security people and people working in the financial industry HSMs are not thought about that often. In particular most network or systems security people would not consider them an option.</description> + </item> + + <item> + <title>How to talk to your microcontroller over serial</title> + <link>http://jaseg.de/blog/serial-protocols/</link> + <pubDate>Sat, 19 May 2018 08:09:46 +0200</pubDate> + + <guid>http://jaseg.de/blog/serial-protocols/</guid> + <description>Scroll to the end for the TL;DR. +In this article I will give an overview on the protocols spoken on serial ports, highlighting common pitfalls. I will summarize some points on how to design a serial protocol that is simple to implement and works reliably even under error conditions. +If you have done low-level microcontroller firmware you will regularly have had to stuff some data up a serial port to another microcontroller or to a computer.</description> + </item> + + <item> + <title>Thor's Hammer</title> + <link>http://jaseg.de/blog/thors-hammer/</link> + <pubDate>Thu, 03 May 2018 11:59:37 +0200</pubDate> + + <guid>http://jaseg.de/blog/thors-hammer/</guid> + <description>In case you were having an inferiority complex because your friends' IBM Model M keyboards are so much louder than the shitty rubber dome freebie you got with your pc... Here's the solution: Thor's Hammer, a simple typing cadence enhancer for PS/2 keyboards. +Your browser does not support the HTML5 video tag. A demonstration of the completed project. h264 download / webm download The connects to the keyboard's PS/2 clock line and briefly actuates a large solenoid on each key press.</description> + </item> + + <item> + <title>32-Channel LED tape driver</title> + <link>http://jaseg.de/blog/multichannel-led-driver/</link> + <pubDate>Wed, 02 May 2018 11:31:14 +0200</pubDate> + + <guid>http://jaseg.de/blog/multichannel-led-driver/</guid> + <description>Theoretical basics Together, a friend and I outfitted the small staircase at Berlin's Chaos Computer Club with nice, shiny RGB-WW LED tape for ambient lighting. This tape is like regular RGB tape but with an additional warm white channel, which makes for much more natural pastels and whites. There are several variants of RGBW tape. Cheap ones have separate RGB and white LEDs, which is fine for indirect lighting but does not work for direct lighting.</description> + </item> + + <item> + <title>Wifi Led Driver</title> + <link>http://jaseg.de/blog/wifi-led-driver/</link> + <pubDate>Wed, 02 May 2018 11:31:03 +0200</pubDate> + + <guid>http://jaseg.de/blog/wifi-led-driver/</guid> + <description>Project motivation The completed driver board installed in the 3D-printed case. This device can now be connected to 12V and two segments of LED tape that can then be controlled trough Wifi. The ESP8266 module goes on the pin header on the left and was removed for this picture. After the multichannel LED driver was completed, I was just getting used to controlling LEDs at 14-bit resolution. I liked the board we designed in this project, but at 32 channels it was a bit large for most use cases.</description> + </item> + + <item> + <title>LED Characterization</title> + <link>http://jaseg.de/blog/led-characterization/</link> + <pubDate>Wed, 02 May 2018 11:18:38 +0200</pubDate> + + <guid>http://jaseg.de/blog/led-characterization/</guid> + <description>Preface Recently, I have been working on a small driver for ambient lighting using 12V LED strips like you can get inexpensively from China. I wanted to be able to just throw one of these somewhere, stick down some LED tape, hook it up to a small transformer and be able to control it through Wifi. When I was writing the firmware, I noticed that when fading between different colors, the colors look all wrong!</description> + </item> + + </channel> +</rss> diff --git a/content/blog/kicad-mesh-plugin/images/anim.webp b/blog/kicad-mesh-plugin/images/anim.webp Binary files differindex a2244d0..a2244d0 100755..100644 --- a/content/blog/kicad-mesh-plugin/images/anim.webp +++ b/blog/kicad-mesh-plugin/images/anim.webp diff --git a/content/blog/kicad-mesh-plugin/images/cells-0.svg b/blog/kicad-mesh-plugin/images/cells-0.svg index f1d881c..f1d881c 100755..100644 --- a/content/blog/kicad-mesh-plugin/images/cells-0.svg +++ b/blog/kicad-mesh-plugin/images/cells-0.svg diff --git a/content/blog/kicad-mesh-plugin/images/cells-100.svg b/blog/kicad-mesh-plugin/images/cells-100.svg index efc4f03..efc4f03 100755..100644 --- a/content/blog/kicad-mesh-plugin/images/cells-100.svg +++ b/blog/kicad-mesh-plugin/images/cells-100.svg diff --git a/content/blog/kicad-mesh-plugin/images/cells-25.svg b/blog/kicad-mesh-plugin/images/cells-25.svg index 670ad1a..670ad1a 100755..100644 --- a/content/blog/kicad-mesh-plugin/images/cells-25.svg +++ b/blog/kicad-mesh-plugin/images/cells-25.svg diff --git a/content/blog/kicad-mesh-plugin/images/cells-50.svg b/blog/kicad-mesh-plugin/images/cells-50.svg index 3b5a0a3..3b5a0a3 100755..100644 --- a/content/blog/kicad-mesh-plugin/images/cells-50.svg +++ b/blog/kicad-mesh-plugin/images/cells-50.svg diff --git a/content/blog/kicad-mesh-plugin/images/cells-75.svg b/blog/kicad-mesh-plugin/images/cells-75.svg index 40e7fc4..40e7fc4 100755..100644 --- a/content/blog/kicad-mesh-plugin/images/cells-75.svg +++ b/blog/kicad-mesh-plugin/images/cells-75.svg diff --git a/content/blog/kicad-mesh-plugin/images/grid-vis-plain.svg b/blog/kicad-mesh-plugin/images/grid-vis-plain.svg index 80b8d84..80b8d84 100755..100644 --- a/content/blog/kicad-mesh-plugin/images/grid-vis-plain.svg +++ b/blog/kicad-mesh-plugin/images/grid-vis-plain.svg diff --git a/content/blog/kicad-mesh-plugin/images/grid-vis.svg b/blog/kicad-mesh-plugin/images/grid-vis.svg index fbcdafa..fbcdafa 100755..100644 --- a/content/blog/kicad-mesh-plugin/images/grid-vis.svg +++ b/blog/kicad-mesh-plugin/images/grid-vis.svg diff --git a/content/blog/kicad-mesh-plugin/images/kicad-mesh-outline.png b/blog/kicad-mesh-plugin/images/kicad-mesh-outline.png Binary files differindex fc0d51e..fc0d51e 100755..100644 --- a/content/blog/kicad-mesh-plugin/images/kicad-mesh-outline.png +++ b/blog/kicad-mesh-plugin/images/kicad-mesh-outline.png diff --git a/content/blog/kicad-mesh-plugin/images/kicad-mesh-result-large.png b/blog/kicad-mesh-plugin/images/kicad-mesh-result-large.png Binary files differindex 798287b..798287b 100755..100644 --- a/content/blog/kicad-mesh-plugin/images/kicad-mesh-result-large.png +++ b/blog/kicad-mesh-plugin/images/kicad-mesh-result-large.png diff --git a/content/blog/kicad-mesh-plugin/images/kicad-mesh-settings.png b/blog/kicad-mesh-plugin/images/kicad-mesh-settings.png Binary files differindex 72e7e25..72e7e25 100755..100644 --- a/content/blog/kicad-mesh-plugin/images/kicad-mesh-settings.png +++ b/blog/kicad-mesh-plugin/images/kicad-mesh-settings.png diff --git a/content/blog/kicad-mesh-plugin/images/kicad-mesh-settings2.png b/blog/kicad-mesh-plugin/images/kicad-mesh-settings2.png Binary files differindex 8da33be..8da33be 100755..100644 --- a/content/blog/kicad-mesh-plugin/images/kicad-mesh-settings2.png +++ b/blog/kicad-mesh-plugin/images/kicad-mesh-settings2.png diff --git a/content/blog/kicad-mesh-plugin/images/maze_tiles.svg b/blog/kicad-mesh-plugin/images/maze_tiles.svg index 4b71f19..4b71f19 100755..100644 --- a/content/blog/kicad-mesh-plugin/images/maze_tiles.svg +++ b/blog/kicad-mesh-plugin/images/maze_tiles.svg diff --git a/content/blog/kicad-mesh-plugin/images/maze_tiles_plain.svg b/blog/kicad-mesh-plugin/images/maze_tiles_plain.svg index be1f8d5..be1f8d5 100755..100644 --- a/content/blog/kicad-mesh-plugin/images/maze_tiles_plain.svg +++ b/blog/kicad-mesh-plugin/images/maze_tiles_plain.svg diff --git a/content/blog/kicad-mesh-plugin/images/modern_art.svg b/blog/kicad-mesh-plugin/images/modern_art.svg index 2729b63..2729b63 100755..100644 --- a/content/blog/kicad-mesh-plugin/images/modern_art.svg +++ b/blog/kicad-mesh-plugin/images/modern_art.svg diff --git a/content/blog/kicad-mesh-plugin/images/tiles-25-small.svg b/blog/kicad-mesh-plugin/images/tiles-25-small.svg index 21b17ed..21b17ed 100755..100644 --- a/content/blog/kicad-mesh-plugin/images/tiles-25-small.svg +++ b/blog/kicad-mesh-plugin/images/tiles-25-small.svg diff --git a/content/blog/kicad-mesh-plugin/images/traces-25-small.svg b/blog/kicad-mesh-plugin/images/traces-25-small.svg index af9a8ef..af9a8ef 100755..100644 --- a/content/blog/kicad-mesh-plugin/images/traces-25-small.svg +++ b/blog/kicad-mesh-plugin/images/traces-25-small.svg diff --git a/blog/kicad-mesh-plugin/index.html b/blog/kicad-mesh-plugin/index.html new file mode 100644 index 0000000..33ddc30 --- /dev/null +++ b/blog/kicad-mesh-plugin/index.html @@ -0,0 +1,217 @@ +<!DOCTYPE html> +<html><head> + <meta charset="utf-8"> + <title>Kicad Mesh Plugin | Home</title> + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="mobile-web-app-capable" content="yes"> + <meta name="color-scheme" content="dark light"> + <link rel="stylesheet" href="/style.css"> +</head> +<body><nav> + <div class="internal"> + + <a href="/" title="Home">Home</a> + <a href="/blog/" title="Blog">Blog</a> + <a href="/projects/" title="Projects">Projects</a> + <a href="/about/" title="About">About</a> + </div> + <div class="external"> + <a href="https://git.jaseg.de/" title="cgit">cgit</a> + <a href="https://github.com/jaseg" title="Github">Github</a> + <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a> + <a href="https://chaos.social/jaseg" title="Mastodon">Mastodon</a> + </span> +</nav> + + <header> + <h1>Kicad Mesh Plugin</h1> +<ul class="breadcrumbs"> + <li><a href="/">jaseg.de</a></li> + <li><a href="/blog/">Blog</a></li><li><a href="/blog/kicad-mesh-plugin/">Kicad Mesh Plugin</a></li> +</ul> + <strong>2020-08-18</strong> + </header> + <main> + <div class="document"> + + +<figure> +<img src="images/anim.webp" style="max-width: 20em"> +</figure><div class="section" id="tamper-detection-meshes"> +<h2>Tamper Detection Meshes</h2> +<p>Cryptography is at the foundation of our modern, networked world. From email to card payment infrastructure in brick and +mortar stores, cryptographic keys secure almost every part of our digital lives againts cybercriminals or curious +surveillance capitalists. Without cryptography, many of the things we routinely do in our lives such as paying for +groceries with a credit card, messaging a friend on <a class="reference external" href="https://signal.org">Signal</a> or unlocking a car with its keyfob +would not be possible. The security of all of these systems in its core lies on the secrecy of cryptographic keys. +Systems differ in what kind of keys they use, how often these keys are replaced and the intricacies of the cryptographic +operations these keys fit into but all have in common that their security relies on keeping the keys secret.</p> +<p>In practice, this secrecy has been implemented in many different ways. Mass-market software such as browsers or +messenger apps usually relies on some operating system facility to tell the computer "<em>please keep this piece of memory +away from all other applications</em>". While on desktop operating systems usually this does not provide much of a barrier +to other programs on the same computer, on modern mobile operating systems this approach is actually quite secure. +However, given sufficient resources no security is perfect. All of these systems can be compromised if the host +operating system is compromised sufficiently, and for organizations with considerable resources a market has sprung up +that offers turn-key solutions for all wiretapping needs.</p> +<p>In some applications, this level of security has not been considered sufficient. Particularly financial infrastructure +is such a high-profile target that a lot of effort has been put into the security of cryptographic implementations. The +best cryptographic algorithm is useless if it is run on a compromised system (from that system's point of view anyway). +One of the core cryptographic components in financial applications are smartcards like they are used as payment cards in +most countries nowadays. These smartcards contain a small, specialized cryptographic microcontroller that is designed to +be hard to tamper with. Though one of the design goals of the system is to reduce the amount of sensitive information +stored on the card, things such as copying of a card can only be hindered by making the chip hard to read out.</p> +<figure> + <img src="images/modern_art.svg" style="max-width: 20em"> +</figure><p>With smartcards being the means of choice on one side of the counter in electronic payments, on the other side of the +counter a different technology prevails. Attacks on payment terminals are bound to have much more dire consequences than +attacks on individual cards since one terminal might see hundreds of cards being read every day. For this reason, the +level of attack countermeasures employed in these terminals is a considerable step up from bare smartcards. While a +smartcard is made physically hard to tamper, it does not have a battery and it can only detect tampering once it is +powered by a reader. This allows for well-equipped attackers to use tools such as Focused Ion Beam (FIB) workstations to +circumvent the smartcard's defences while it is powered down, and then power up the card to carry out the actual attack.</p> +<p>The answer to this problem in electronic payment infrastructure is called <em>Hardware Security Module</em>, or HSM. An HSM is +similar to a smartcard in its function (cryptographic processing using keys that are meant to never leave the protection +of the HSM). The one major between the two is that an HSM has its own battery and is continuously powered from its +manufacture to the day it is scrapped. If the HSM looses power at any point in time, it uses a small amount of energy +stored internally to securely wipe all cryptographic secrets from its memory within a few milliseconds.</p> +<p>Being powered at all times allows the HSM to actively detect and respond to attacks. The most common way this is done is +by wrapping the juicy secret parts in a foil or a printed circuit board that is patterned with a long and convoluted +maze of wires, called a <em>mesh</em>. The HSM is continuously monitoring these wires for changes (such as shorts, breaks or +changes in resistance) and will sound the alarm when any are detected. Practically, this presents a considerable hurdle +to any attacker: They have to find a way to disable or circumvent the mesh while it is being monitored by the HSM. In +practice, often this is no insurmountable challenge but it again increases attack costs.</p> +</div> +<div class="section" id="diy-meshes"> +<h2>DIY Meshes</h2> +<p>Throughout my studies in security research I have always had an interest in HSMs. I have taken apart my fair share of +HSMs and at this point, to understand the technology more, I want to experiment with building my own HSM. In last year's +<a class="reference external" href="http://jaseg.de/blog/hsm-basics/">HSM basics</a> post I have lined out some ideas for a next generation design that +deviates from the bread-and-butter apporoach of using a mesh as the primary security feature. Before embarking on +practical experiments with these ideas, I want to first take a stab at replicating the current state of the art as best +I can. State of the art meshes often use exotic substrates such as 3D plastic parts with traces chemically deposited on +their surface or special flexible substrates with conductive ink traces. These technologies will likely be too +cumbersome for me to implement myself only for a few prototypes, and industrial manufacturers will most likely be too +expensive. Thus, I will concentrate on regular PCB technology for now.</p> +<p>The idea of a mesh on a PCB is pretty simple: You have one or several traces that you try to cover every corner of the +mesh PCB's area with. To be most effective, the traces should be as thin and as close together as possible. To increase +the chances of a manipulation being detected, multiple traces can also be used that can then be monitored for shorts +between them.</p> +<p>While one can feasibly lay out these traces by hand, this really is an ideal application of a simple auto-router. While +general PCB autorouting is <em>hard</em>, auto-routing just a few traces to approximate a space-filling curve is not. Since I +am just starting out, I went with the simplest algorithmic solution I could think of. I first approximate the area +designated to the mesh with a square grid whose cells are a multiple of my trace/space size. The mesh will only be drawn +into grid cells that are fully inside the set boundaries. All cells outside or going across the border are discarded in +this step.</p> +<p>I decided to implement this auto-router in a KiCAD plugin. Though KiCADs plugin API is not the best, it was just about +usable for this task.</p> +<figure> + <img src="images/kicad-mesh-outline.png" alt="KiCAD showing an irregular board shape with rounded corners and + indents. In the middle of the board there is a footprint for a 4-pin surface-mount pin header."> + <figcaption>The process starts out with the mesh shape being defined inside KiCAD. The mesh's outline is drawn + onto one of the graphical "Eco" layers. A footprint is placed to serve as a placeholder for the mesh's + connections to the outside world. This footprint is later used as the starting point for the mesh generation + algorithm.</figcaption> +</figure><figure> + <img src="images/grid-vis-plain.svg" alt="A vizualization of the grid fitting process. Over the mesh's irregular + outline a grid is drawn. In this picture, all grid cells that are fully inside the grid are shown. Grid cells + that overlap the mesh border are highlighted. Grid cells outside of the mesh border are not drawn."> + <figcaption>A visualization of the grid fitting process. First, a grid large enough to contain the mesh border + is generated. Then, every cell is checked for overlap with the mesh border area. If the cell is fully inside, it + (yellow), it is considered in the mesh generation later. Cells outside (gray) or on the border (red) are + discarded.</figcaption> +</figure><p>After generating the grid, starting from the place I want to connect to the mesh, I walk the grid's cells one by one to +generate a tree that covers the entire grid's area. To set the mesh's starting place I place a footprint on the board +(dark gray in the picture above) whose designator I then tell my script. The tree generation algorithm looks like a +depth-first search, except all checks are random. Depending on the level of randomness used at each step of the +algorithm it yields more or less organized-looking results. Below are five example runs of the algorithm at differing +levels of randomness with the cells colored according to their distance from the tree root. 0% randomness means that the +algorithm is going to try cells in forward direction first on every step, and only then try out left and right. 100% +means that on every step, the algorithm is choosing a new direction at random.</p> +<div class="subfigure"> + <figure> + <img src="images/cells-0.svg" alt="a completely organized looking grid with spiral patterns all over."> + <figcaption>0%</figcaption> + </figure> + <figure> + <img src="images/cells-25.svg"> + <figcaption>25%</figcaption> + </figure> + <figure> + <img src="images/cells-50.svg"> + <figcaption>50%</figcaption> + </figure> + <figure> + <img src="images/cells-75.svg"> + <figcaption>75%</figcaption> + </figure> + <figure> + <img src="images/cells-100.svg" alt="a completely random looking grid with cells aggregating into ireggular + areas that look like paint splotches."> + <figcaption>100%</figcaption> + </figure> +</div><p>After I have built this tree like you would do in a depth-first search, I draw my one or several mesh mesh traces into +it. The core observation here is that there is only 16 possible ways a cell can be connected: It has four neighbors, +each of which it can either be connected to or not, which results in 2^4 options. If you consider rotations and +mirroring, this works out to rotations or mirrored versions of only six base tiles: The empty tile, a tile with all four +sides connected, a straight through, a 90 degree bend, and a "T"-junction—see the illustration below.</p> +<figure> + <img src="images/maze_tiles_plain.svg" style="max-width: 20em"> + <figcaption> + There are six possible tile types in our connectivity graph inside its square tiling. This graphic illustrates + all sixteen rotations of these with how they would look in a two-conductor mesh. + </figcaption> +</figure><p>After tiling the grid according to the key above, we get the result below.</p> +<figure> + <img src="images/tiles-25-small.svg"> + <figcaption> + An auto-routed mesh with traces colored according to tile types. + </figcaption> +</figure><figure> + <img src="images/traces-25-small.svg"> + <figcaption> + The same mesh, but with traces all black. + </figcaption> +</figure><p>Putting it all together got me the KiCAD plugin you can see in the screenshot below.</p> +<figure> + <img src="images/kicad-mesh-settings2.png"> + <figcaption> + The plugin settings window open. + </figcaption> +</figure><figure> +<img src="images/kicad-mesh-result-large.png"> +<figcaption> + After runing the plugin, the generated mesh looks like this in pcbnew. +</figcaption> +</figure><p>I am fairly happy with the result, but getting there was a medium pain. Especially KiCAD's plugin API is still very +unfinieshed. It is hard to use, most parts are completely undocumented and if you use anything but its most basic parts +things tend to break. One particular pain point for me was that after generating the mesh, the traces have been added to +the board, but are still invisible for some reason. You have to save the board first, then re-load the file for them to +become visible. Also KiCAD crashes whenever the plugin tries to remove a trace, so currently my workflow involves always +making a copy of the board file first and treating mesh generation as a non-reversible finishing step.</p> +<p><a class="reference external" href="https://git.jaseg.de/kimesh.git/tree/plugin/mesh_dialog.py">Check out the code on my cgit</a>.</p> +<!-- :: + +.. raw:: html + + <figure> + <img src="images/grid-vis-plain.svg" alt=""> + <figcaption></figcaption> + </figure> --> +</div> +</div> + </main><footer> + Copyright © 2023 Jan Sebastian Götte + / <a href="/about/">About</a> + / <a href="/imprint/">Imprint</a> +</footer> +<script> + if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser + contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to + prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition. + In support of an open web, this website does not function with this DRM. Please install a browser such + as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports + ad blockers.`; + </script> + </body> +</html> diff --git a/content/blog/led-characterization/images/daylight_spectrum_dvd.jpg b/blog/led-characterization/images/daylight_spectrum_dvd.jpg Binary files differindex d01242e..d01242e 100644 --- a/content/blog/led-characterization/images/daylight_spectrum_dvd.jpg +++ b/blog/led-characterization/images/daylight_spectrum_dvd.jpg diff --git a/content/blog/led-characterization/images/driver_ringing_strong.jpg b/blog/led-characterization/images/driver_ringing_strong.jpg Binary files differindex 0419a0e..0419a0e 100644 --- a/content/blog/led-characterization/images/driver_ringing_strong.jpg +++ b/blog/led-characterization/images/driver_ringing_strong.jpg diff --git a/content/blog/led-characterization/images/driver_ringing_weak.jpg b/blog/led-characterization/images/driver_ringing_weak.jpg Binary files differindex 12f9c5d..12f9c5d 100644 --- a/content/blog/led-characterization/images/driver_ringing_weak.jpg +++ b/blog/led-characterization/images/driver_ringing_weak.jpg diff --git a/content/blog/led-characterization/images/electronics_whole.jpg b/blog/led-characterization/images/electronics_whole.jpg Binary files differindex faaf751..faaf751 100644 --- a/content/blog/led-characterization/images/electronics_whole.jpg +++ b/blog/led-characterization/images/electronics_whole.jpg diff --git a/content/blog/led-characterization/images/hsv_cylinder.png b/blog/led-characterization/images/hsv_cylinder.png Binary files differindex 265f3e0..265f3e0 100644 --- a/content/blog/led-characterization/images/hsv_cylinder.png +++ b/blog/led-characterization/images/hsv_cylinder.png diff --git a/content/blog/led-characterization/images/photodiode_sensitivity.svg b/blog/led-characterization/images/photodiode_sensitivity.svg index e845444..e845444 100644 --- a/content/blog/led-characterization/images/photodiode_sensitivity.svg +++ b/blog/led-characterization/images/photodiode_sensitivity.svg diff --git a/content/blog/led-characterization/images/preamp_back.jpg b/blog/led-characterization/images/preamp_back.jpg Binary files differindex 0af495d..0af495d 100644 --- a/content/blog/led-characterization/images/preamp_back.jpg +++ b/blog/led-characterization/images/preamp_back.jpg diff --git a/content/blog/led-characterization/images/preamp_front.jpg b/blog/led-characterization/images/preamp_front.jpg Binary files differindex 62fad28..62fad28 100644 --- a/content/blog/led-characterization/images/preamp_front.jpg +++ b/blog/led-characterization/images/preamp_front.jpg diff --git a/content/blog/led-characterization/images/preamp_schematic.jpg b/blog/led-characterization/images/preamp_schematic.jpg Binary files differindex 6be7bbd..6be7bbd 100644 --- a/content/blog/led-characterization/images/preamp_schematic.jpg +++ b/blog/led-characterization/images/preamp_schematic.jpg diff --git a/content/blog/led-characterization/images/processed_plot_cheap_rgb.svg b/blog/led-characterization/images/processed_plot_cheap_rgb.svg index 9745172..9745172 100644 --- a/content/blog/led-characterization/images/processed_plot_cheap_rgb.svg +++ b/blog/led-characterization/images/processed_plot_cheap_rgb.svg diff --git a/content/blog/led-characterization/images/raw_plot_cheap_rgb.svg b/blog/led-characterization/images/raw_plot_cheap_rgb.svg index 895569f..895569f 100644 --- a/content/blog/led-characterization/images/raw_plot_cheap_rgb.svg +++ b/blog/led-characterization/images/raw_plot_cheap_rgb.svg diff --git a/content/blog/led-characterization/images/rgb_cube.svg b/blog/led-characterization/images/rgb_cube.svg index 8af7a00..8af7a00 100644 --- a/content/blog/led-characterization/images/rgb_cube.svg +++ b/blog/led-characterization/images/rgb_cube.svg diff --git a/content/blog/led-characterization/images/spectrograph_step1_parts.jpg b/blog/led-characterization/images/spectrograph_step1_parts.jpg Binary files differindex 107220a..107220a 100644 --- a/content/blog/led-characterization/images/spectrograph_step1_parts.jpg +++ b/blog/led-characterization/images/spectrograph_step1_parts.jpg diff --git a/content/blog/led-characterization/images/spectrograph_step2.jpg b/blog/led-characterization/images/spectrograph_step2.jpg Binary files differindex b678372..b678372 100644 --- a/content/blog/led-characterization/images/spectrograph_step2.jpg +++ b/blog/led-characterization/images/spectrograph_step2.jpg diff --git a/content/blog/led-characterization/images/spectrograph_step3.jpg b/blog/led-characterization/images/spectrograph_step3.jpg Binary files differindex acd6d5e..acd6d5e 100644 --- a/content/blog/led-characterization/images/spectrograph_step3.jpg +++ b/blog/led-characterization/images/spectrograph_step3.jpg diff --git a/content/blog/led-characterization/images/spectrograph_step4_complete.jpg b/blog/led-characterization/images/spectrograph_step4_complete.jpg Binary files differindex d23560d..d23560d 100644 --- a/content/blog/led-characterization/images/spectrograph_step4_complete.jpg +++ b/blog/led-characterization/images/spectrograph_step4_complete.jpg diff --git a/content/blog/led-characterization/images/zeus_hammer_breadboard.jpg b/blog/led-characterization/images/zeus_hammer_breadboard.jpg Binary files differindex 08efebb..08efebb 100644 --- a/content/blog/led-characterization/images/zeus_hammer_breadboard.jpg +++ b/blog/led-characterization/images/zeus_hammer_breadboard.jpg diff --git a/content/blog/led-characterization/images/zeus_hammer_breadboard_original.jpg b/blog/led-characterization/images/zeus_hammer_breadboard_original.jpg Binary files differindex 4f8f34e..4f8f34e 100644 --- a/content/blog/led-characterization/images/zeus_hammer_breadboard_original.jpg +++ b/blog/led-characterization/images/zeus_hammer_breadboard_original.jpg diff --git a/content/blog/led-characterization/images/zeus_hammer_schematic.jpg b/blog/led-characterization/images/zeus_hammer_schematic.jpg Binary files differindex 0e6f483..0e6f483 100644 --- a/content/blog/led-characterization/images/zeus_hammer_schematic.jpg +++ b/blog/led-characterization/images/zeus_hammer_schematic.jpg diff --git a/content/blog/led-characterization/images/zeus_hammer_schematic_original.jpg b/blog/led-characterization/images/zeus_hammer_schematic_original.jpg Binary files differindex b50b3a0..b50b3a0 100644 --- a/content/blog/led-characterization/images/zeus_hammer_schematic_original.jpg +++ b/blog/led-characterization/images/zeus_hammer_schematic_original.jpg diff --git a/blog/led-characterization/index.html b/blog/led-characterization/index.html new file mode 100644 index 0000000..14ffba4 --- /dev/null +++ b/blog/led-characterization/index.html @@ -0,0 +1,431 @@ +<!DOCTYPE html> +<html><head> + <meta charset="utf-8"> + <title>LED Characterization | Home</title> + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="mobile-web-app-capable" content="yes"> + <meta name="color-scheme" content="dark light"> + <link rel="stylesheet" href="/style.css"> +</head> +<body><nav> + <div class="internal"> + + <a href="/" title="Home">Home</a> + <a href="/blog/" title="Blog">Blog</a> + <a href="/projects/" title="Projects">Projects</a> + <a href="/about/" title="About">About</a> + </div> + <div class="external"> + <a href="https://git.jaseg.de/" title="cgit">cgit</a> + <a href="https://github.com/jaseg" title="Github">Github</a> + <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a> + <a href="https://chaos.social/jaseg" title="Mastodon">Mastodon</a> + </span> +</nav> + + <header> + <h1>LED Characterization</h1> +<ul class="breadcrumbs"> + <li><a href="/">jaseg.de</a></li> + <li><a href="/blog/">Blog</a></li><li><a href="/blog/led-characterization/">LED Characterization</a></li> +</ul> + <strong>2018-05-02</strong> + </header> + <main> + <div class="document"> + + +<div class="section" id="preface"> +<h2>Preface</h2> +<p>Recently, I have been working on a <a class="reference external" href="http://jaseg.de/blog/wifi-led-driver/">small driver</a> for ambient lighting using 12V LED strips like you can get +inexpensively from China. I wanted to be able to just throw one of these somewhere, stick down some LED tape, hook it up +to a small transformer and be able to control it through Wifi. When I was writing the firmware, I noticed that when +fading between different colors, the colors look <em>all wrong</em>! This observation led me down a rabbit hole of color +perception and LED peculiarities.</p> +<p>The idea of the LED driver was that it can be used either with up to eight single-color LED tapes or, much more +interesting, with up to two RGB or RGBW (red-green-blue-white) LED tapes. For ambient lighting high color resolution was +really important so you could dim it down a lot without flickering. I ended up using the same driver stage I used in the +<a class="reference external" href="http://jaseg.de/blog/multichannel-led-driver/">multichannel LED driver</a> project for its great color resolution and low hardware requirements.</p> +<figure> + <img src="images/rgb_cube.svg" alt="An illustration of the RGB color cube."> + <figcaption>An illustration of the RGB color cube. + <a href="https://commons.wikimedia.org/wiki/File:RGB_color_cube.svg">Picture</a> by + <a href="https://commons.wikimedia.org/wiki/User:Maklaan">Maklaan from Wikimedia Commons</a>, + <a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA 3.0</a> + </figcaption> +</figure><p>To make setting colors over Wifi more intuitive I implemented support for HSV colors. RGB is fine for communication +between computers, but I think HSV is easier to work with when manually inputting colors from the command line. RGB is +close to how most monitors, cameras and the human visual apparatus work on a very low level but doesn't match +higher-level human color perception very well. When we describe a color we tend to think in terms of "hue" or +"brightness", and computing a measure of those from RGB values is not easy.</p> +</div> +<div class="section" id="colors-and-color-spaces"> +<h2>Colors and Color Spaces</h2> +<p><a class="reference external" href="https://en.wikipedia.org/wiki/Color_space">Color spaces</a> are a mathematical abstraction of the concept of color. When we say "RGB", most of the time we actually +mean <a class="reference external" href="https://en.wikipedia.org/wiki/SRGB">sRGB</a>, a standardized notion of how to map three numbers labelled "red", "green" and "blue" onto a perceived +color. <a class="reference external" href="https://en.wikipedia.org/wiki/HSL_and_HSV">HSV</a> is an early attempt to more closely align these numbers with our perception. After HSV, a number of other +<em>perceptual</em> color spaces such as <a class="reference external" href="https://en.wikipedia.org/wiki/CIE_1931_color_space">XYZ (CIE 1931)</a> and <a class="reference external" href="https://en.wikipedia.org/wiki/Lab_color_space">CIE Lab/LCh</a> were born, further improving this alignment. In +this mathematical model, mapping a color from one color space into another color space is just a coordinate +transformation.</p> +<figure> + <img src="images/hsv_cylinder.png" alt="An illustration of the HSV color space as a cylinder."> + <figcaption>An illustration of the HSV color space as a cylinder. + <a href="https://commons.wikimedia.org/wiki/File:HSV_color_solid_cylinder.png">Picture</a> by + <a href="https://commons.wikimedia.org/wiki/User:SharkD">SharkD from Wikimedia Commons</a>, + <a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA 3.0</a> + </figcaption> +</figure><p>CIE 1931 XYZ is much larger than any other color space, which is why it is a good basis to express other color spaces +in. In XYZ there are many coordinates that are outside of what the human eye can perceive. Below is an illustration of +the sRGB space within XYZ. The wireframe cube is (0,0,0) to (1,1,1) in XYZ. The colorful object in the middle is what +of sRGB fits inside XYZ, and the lines extending out from it indicate the space that can be expressed in sRGB but not in +XYZ. The fat white curve is a projection of the <em>monochromatic spectral locus</em>, that is the curve of points you get in +XYZ for pure visible wavelengths.</p> +<p>As you can see, sRGB is <em>much</em> smaller than XYZ or even the part within the monochromatic locus that we can perceive. In +particular in the blues and greens we loose <em>a lot</em> of colors to sRGB.</p> +<figure> + <video controls loop> + <source src="video/sRGB.mkv" type="video/h264"> + <source src="video/sRGB.webm" type="video/webm"> + Your browser does not support the HTML5 video tag. + </video> + <figcaption>Illustration of the measured sRGB color space within XYZ. The thick, white line is the spectral + locus. + + <a href="video/sRGB.mkv">mkv/h264 download</a> / + <a href="video/sRGB.webm">webm download</a> + </figcaption> +</figure><p>The wrong colors I got when fading between colors were caused by this coordinate transformation being askew. Thinking +over the problem, there are several sources for imperfections:</p> +<ul class="simple"> +<li>The LED driver may not be entirely linear. For most modulations such as PWM the brightness will be linear starting +from a certain value, but there is probably an offset caused by imperfect edges of the LED current. This offset can be +compensated with software calibration. I built a calibration setup for driver linearity in the <a class="reference external" href="http://jaseg.de/blog/multichannel-led-driver/">multichannel LED +driver</a> project. Below are pictures of ringing on the edges of an LED driver's waveform.</li> +<li>The red, green and blue channels of the LEDs used on the LED tape are not matched. This skews the RGB color space. +In practice, the blue channel of my RGB tape to me <em>looks</em> much brighter than the red channel.</li> +<li>The precise colors of the red, green and blue channels of the LEDs are unknown. Though the red channel <em>looks</em> red, it +may be of a slightly different hue compared to the reference red used in <a class="reference external" href="https://en.wikipedia.org/wiki/SRGB">sRGB</a> which would also skew the RGB color +space.</li> +</ul> +<div class="subfigure"> + <figure> + <img src="images/driver_ringing_strong.jpg" alt="Strong ringing on the LED voltage waveform edge at about + 100% overshoot during about 70% of the cycle time."> + <figcaption>The LED strip being at the end of a couple meters of wire caused extremely bad ringing at high + driving frequencies.</figcaption> + </figure> + <figure> + <img src="images/driver_ringing_weak.jpg" alt="Weak ringing on the LED voltage waveform edge at about 30% + overshoot during about 20% of the cycle time."> + <figcaption>Adding a resistor in front of the MOSFET gate to slow the transition dampened the ringing + somewhat, but ultimately it cannot be eliminated entirely.</figcaption> + </figure> +</div><p>These last two errors are tricky to compensate. What I needed for that was basically a model of the <em>perceived</em> colors +of the LED tape's color channels. A way of doing his is to record the spectra of all color channels and then evaluate +their respective XYZ coordinates. If all three channels are measured in one go with the same setup the relative +magnitudes of the channels in XYZ will be accurate.</p> +<p>To map any color to the LEDs, the color's XYZ coordinates simply have to be mapped onto the linear coordinate system +produced by these three points within XYZ. LEDs are mostly linear in their luminous flux vs. current characteristic so +this model will be adequate. The spectral integrals mapping the channels' measured responses to XYZ need only be +calculated once and their results can be used as scaling factors thereafter.</p> +</div> +<div class="section" id="measuring-the-spectrum"> +<h2>Measuring the spectrum</h2> +<p>In order to compensate for the cheap LED tape's non-ideal performance I had to measure the LED's red, green and blue +channels' spectra. The obvious thing would be to go out and buy a <a class="reference external" href="https://en.wikipedia.org/wiki/Ultraviolet%E2%80%93visible_spectroscopy">spectrograph</a>, or ask someone to borrow theirs. The +former is kind of expensive, and I did not want to wait two weeks for the thing to arrive. The latter I could probably +not do every time I got new LED tape. Thus the only choice was to build my own.</p> +<p>Luckily, building your own spectrometer is really easy. The first thing you need is something that splits incident light +into its constituent wavelengths. In professional devices this is called the <em>`monochromator`_</em>, since it allows extraction +of small color bands from the spectrum. The second thing is some sort of optics that project the incident light onto a +screen behind the monochromator. In professional devices lenses or curved mirrors are used. In a simple homebrew job a +pinhole as you would use in a <a class="reference external" href="https://en.wikipedia.org/wiki/Pinhole_camera">camera obscura</a> does a remarkably nice job.</p> +<p>For the monochromator component several things could be used. A prism would work, but I did not have any. The +alternative is a <a class="reference external" href="https://en.wikipedia.org/wiki/Diffraction_grating">diffraction grating</a>. Professional gratings are quite specialized pieces of equipment and thus +rather expensive. Luckily, there is a common household item that works almost as well: A regular CD or DVD. The +microscopic grooves that are used to record data in a CD or DVD work the same as the grooves in a professional +diffraction grating.</p> +</div> +<div class="section" id="household-spectra"> +<h2>Household spectra</h2> +<p>From this starting point, a few seconds on my favorite search engine yielded an <a class="reference external" href="http://www.candac.ca/candacweb/sites/default/files/BuildaSpectroscope.pdf">article by two researchers from the +National Science Museum in Tokyo</a> providing a nice blueprint for a simple cardboard-and-DVD construction for use in +classrooms. I replicated their device using a DVD and it worked beautifully. Daylight and several types of small LEDs I +had around did show the expected spectra. Small red, yellow, green, and blue LEDs showed narrow spectra, daylight one +continuous broad one, and white LEDs a continuous broad one with a distinct bright spot in the blue part. The +single-color LED spectra are quite narrow since they are determined by the LED's semiconductor's band gap, which is +specific to the semiconductor used and is quite precise. White LEDs are in fact a blue LED chip covered with a so-called +<em>phosphor</em>. This phosphor is not elementary phosphorus but an anorganic compound that absorbs the LED chip's blue light +and re-emits a broader spectrum of more yellow-ish wavelengths instead. The final LED spectrum is a superposition of +both spectra, with some of the original blue light leaking through the phosphor mixing with the broadband yellow +spectrum of the phosphor.</p> +<div class="subfigure"> + <figure> + <img src="images/spectrograph_step1_parts.jpg"> + <figcaption>The ingredients. The cup of coffee and Madoka Magica DVD set are essential to the eventual + function of the appartus.</figcaption> + </figure> + <figure> + <img src="images/spectrograph_step2.jpg"> + <figcaption>Step 1: Cut to size and mark down all holes as described in <a + href="http://www.candac.ca/candacweb/sites/default/files/BuildaSpectroscope.pdf">the manual</a></figcaption> + </figure> + <figure> + <img src="images/spectrograph_step3.jpg"> + <figcaption>Step 2: Cut out all holes</figcaption> + </figure> + <figure> + <img src="images/spectrograph_step4_complete.jpg"> + <figcaption>The finished result with the back side showing. The viewing window is on the bottom of the other + side.</figcaption> + </figure> +</div><p>Now that I had a spectrograph, I needed a somewhat predictable way of measuring the spectrum it gave me.</p> +</div> +<div class="section" id="measuring-a-spectrum"> +<h2>Measuring a spectrum</h2> +<p>Pointing a camera at the spectrograph would be the obvious thing to do. This produces pretty images but has one critical +flaw: I wanted to acquire quantitative measurements of brightness across the spectrum. Since I don't have a precise +technical datasheet specifying the spectral response of any of my cameras I can't compare the absolute brightness of +different colors on their pictures. Some other sensor was needed.</p> +<figure> + <img src="images/daylight_spectrum_dvd.jpg"> + <figcaption>The daylight spectrum as seen using a DVD as a grating. + <a href="https://commons.wikimedia.org/wiki/File:SpectresSolaires-DVD.jpg">Picture</a> by + <a href="https://commons.wikimedia.org/wiki/User:Xofc">Xofc from Wikimedia Commons</a>, + <a href="https://creativecommons.org/licenses/by-sa/4.0/">CC-BY-SA 4.0</a> + </figcaption> +</figure><div class="section" id="measuring-light-intensity"> +<h3>Measuring light intensity</h3> +<p>Looking around my lab, I found a bag of <a class="reference external" href="https://dammedia.osram.info/media/resource/hires/osram-dam-2495903/SFH%202701.pdf">SFH2701</a> visible-light photodiodes. Their +datasheet includes their spectral response so I can compensate for that, allowing precise-ish absolute intensity +measurements. Just like LEDs, photodiodes are extremely linear across several orders of magnitude. The datasheet of the +classic <a class="reference external" href="http://www.vishay.com/docs/81521/bpw34.pdf">BPW34</a> photodiode shows that this photodiode's light current is exactly proportional to illuminance over at +least three orders of magnitude. The <a class="reference external" href="https://dammedia.osram.info/media/resource/hires/osram-dam-2495903/SFH%202701.pdf">SFH2701</a> datasheet does not include a similar graph but its performance will be +similar. The <a class="reference external" href="https://dammedia.osram.info/media/resource/hires/osram-dam-2495903/SFH%202701.pdf">SFH2701</a> photodiodes I had at hand were perfect for the job compared to the vintage <a class="reference external" href="http://www.vishay.com/docs/81521/bpw34.pdf">BPW34</a> since their +active sensing area is really small (0.6mm by 0.6mm) compared to the BPW34 (a whopping 3mm by 3mm). If I were to use a +<a class="reference external" href="http://www.vishay.com/docs/81521/bpw34.pdf">BPW34</a> I would have to insert some small apterture in front of it so it does not catch too broad a part of the +spectrum at once. The <a class="reference external" href="https://dammedia.osram.info/media/resource/hires/osram-dam-2495903/SFH%202701.pdf">SFH2701</a> is small enough that if I just point it at the projected spectrum directly I will +already get only a small part of the spectrum inside its 0.6mm active area.</p> +<p>To convert the photodiode's tiny photocurrent into a measurable voltage I built another copy of the <a class="reference external" href="https://en.wikipedia.org/wiki/Transimpedance_amplifier">transimpedance +amplifier</a> circuit I already used in the <a class="reference external" href="http://jaseg.de/blog/multichannel-led-driver/">multichannel LED driver</a>. A <a class="reference external" href="https://en.wikipedia.org/wiki/Transimpedance_amplifier">transimpedance amplifier</a> is an +amplifiert that produces a large voltage from a small current. The weird name comes from the fact that it works kind of +like an amplified resistor (which can be generalized as an <em>impedance</em> electrically). Apply a current to a resistor and +you get a voltage. A transimpedance amplifiert does the same with the difference that its input always stays at 0V, +making it look like an ideal current sink to the connected current source.</p> +<p>Transimpedance amplifiers are common in optoelectronics to convert small photocurrents to voltages. In this instance I +built a very simple circuit with a dampened transimpedance amplifier stage followed by a simple RC filter for noise +rejection and a regular non-inverting amplifier using another op-amp from the same chip to further boost the filtered +transimpedance amplifier output. I put all the passives setting amplifier response (the gain-setting resistors and the +filter resistor and capacitors) on a small removable adapter so I could easily change them if necessary. I put a small +trimpot on the virtual ground both amplifers use as a reference so I could trim that if necessary.</p> +<figure> + <img src="images/preamp_schematic.jpg" alt="A drawing of the photodiode preamplifier's schematic"> + <figcaption>The photodiode preamplifier schematic. Schematic drawn with an unlicensed copy of + DaveCAD.</figcaption> +</figure><p>Following are pictures of the preamplifier board. The connectors on the top-left side are two copies of the analog +signal for the ADC and a small panel meter. The SMA connector is used as the photodiode input since coax cables are +generally low-leakage and have built-in shielding. The circuit is powered via the micro-USB connector and the analog +ground bias voltage can be adjusted using the trimpot.</p> +<p>For easy replacement, all passives setting gain and frequency response are on a small, pluggable carrier PCB made from a +SMD-to-DIP adapter.</p> +<p>Flying-wire construction is just fine for this low-frequency circuit. In a high-speed photodiode preamp, the +transimpedance amplifier circuit would be highly sensitive to stray capacitance, but we're not aiming at high speed +here.</p> +<div class="subfigure"> + <figure> + <img src="images/preamp_front.jpg"> + <figcaption>The front side of the preamplifier board.</figcaption> + </figure> + <figure> + <img src="images/preamp_back.jpg"> + <figcaption>The wiring of the photodiode preamp.</figcaption> + </figure> +</div><p>Given a way to measure intensity what remains missing is a way to scan a single photodiode across the spectrum.</p> +</div> +<div class="section" id="scanning-the-projection"> +<h3>Scanning the projection</h3> +<p>A cheap linear stage can be found in any old CD or DVD drive. These drives use a small linear stage based on a +stepper-driven screw to move the laser unit radially. Removing the laser unit and connecting a leftover stepper driver +module I was left with a small linear stage with about 45 steps per cm without microstepping enabled. The driver I used +was an <a class="reference external" href="https://www.pololu.com/file/0J450/A4988.pdf">A4988</a> module that required at least 8V motor drive voltage. I used a small micro USB-input boost converter +module to generate a stable 10V supply for the motor driver, with the USB's 5V rail used as a logic supply for the motor +driver.</p> +<p>The <a class="reference external" href="https://dammedia.osram.info/media/resource/hires/osram-dam-2495903/SFH%202701.pdf">SFH2701</a> can easily be mounted to the linear stage using a small SMD breakout board glued in place with thin wires +connecting it to the transimpedance amplifier. The DVD drive linear stage is not very strong so it is important that +this wire does not put too much strain on it.</p> +<p>Above the photodiode, I mounted a small piece of paper on the linear stage to be used as a projection screen to align +the linear stage in front of the spectrometer viewing window. A line on the screen paper points to the photodiode die in +parallel to the linear stage allowing precise alignment.</p> +<p>The whole unit with photodiode preamplifier, linear stage, photodiode and stepper motor driver finally looks like this:</p> +<figure> + <img src="images/electronics_whole.jpg" alt="The complete electronics setup of the spectrograph. In the back + there is the DVD drive stepper stage. In front of it, mounted on a piece of wood are a small USB-to-12V + switching-regulator module to power the stepper motor in the top left, below on the bottom left is the + photodiode preamp and on the right is a breadboard with the stepper driver module and lots of jumper wires + interconnecting everything. On the right of the breadboard, a buspirate is attached to interface everything to a + computer. On the bottom edge of the piece of wood, two LED panel meters are mounted for readout of the preamp + output and the stepper supply voltages."> + <figcaption>The complete electronics setup. The buspirate on the right interfaces to a computer and controls the + stepper driver and ADC'es the preamp output. The two panel meters show the preamp output and stepper voltage for + setup.</figcaption> +</figure><p>The projection of the spectrum can be adjusted by moving the light source relative to the entry slot and by moving +around the grating DVD.</p> +</div> +<div class="section" id="the-capture-process"> +<h3>The capture process</h3> +<p>To capture a spectrum, first the light source has to be mounted near the spectrograph's entry slot. The LED tape I +tested I just taped face-down directly into it. Next, the grating DVD has to be adjusted to make sure the spectrum +covers a sensible part of the photodiode's path. Mostly, this boils down to adjusting the photodiode distance and height +to match the vertical extent and wiggling the grating DVD to adjust the projection's horizontal position.</p> +<p>After the optics are set-up, the photodiode preamplifier has to be adjusted. In my experiments, most LED tape at 5GΩ +required a high-ish amplification. The goal in this step is to maximize the peak response of the preamp to be just +shy of its VCC rail to make best use of its dynamic range. To adjust the pre-amp, I took several very coarsely-spaced +measurements to give me an estimate of the peak while I did not yet know its precise location.</p> +<p>Since stray daylight totally swamped out the weak projection of the LED's spectrum I shielded the entire setup with a +small box made of black cardboard and two black t-shirts on top. This shielding proved adequate for all my measurements +but I had to be careful not to accidentially move the DVD that was stuck into the spectrograph with the shielding +t-shirts.</p> +<p>For capturing a single spectrum I wrote a small python script that will automatically move the stepper in adjustable +intervals and take two measurements at each point, one with the LED tape off that can be used for offset calibration and +one with the LED tape on. All measurements are stored in a sqlite database that can then be accesssed from other +scripts.</p> +<p>I built a small script that shows the progress of the current run and an jupyter notebook for data analysis. The jupyter +notebook is capable of live-updating a graph with the in-progress spectrum's data. This was quite useful as a sanity +check for when I made some mistake easy to spot in the resulting data.</p> +<p>After one color channel is captured, the LED tape has to be manually set to the next color and the next measurement can +begin.</p> +<figure> + <img src="images/raw_plot_cheap_rgb.svg" alt="A plot with three wide peaks, two large peaks on both sides and + one smaller one in the middle. The middle one overlaps the two on the sides. The large ones are about 2.5V in + amplitude. Overall, the plot is about 300 stepper steps wide with each peak being around 130 steps wide."> + <figcaption>A plot of the raw preamp output voltage versus stepper position. From left to right, the three peaks + are blue, green and red. Step 0 corresponds to the bottommost stepper position and the shortest wavelength. + </figcaption> +</figure></div> +<div class="section" id="data-analysis"> +<h3>Data analysis</h3> +<p>Data analysis consists of three major steps: Offset- and stray light removal, wavelength and amplitude calibration and +color space mapping.</p> +<div class="section" id="offset-removal"> +<h4>Offset removal</h4> +<p>The first task is to remove the offset caused by dark current as well as stray light of the LED's bright primary +reflection on the DVD. The LED is very bright and only a small part of its light gets reflected by the grating towards +the photodiode screen. The remaining part of the light is reflected onto the table in front of the DVD spectrograph. +Though I covered all of this with black cardboard, some of that light ultimately gets reflected onto the photodiode. +This causes a large offset, in particular in the blue part of the spectrum since in this part the photodiode is closest +to the spectrograph's opening.</p> +<p>The composite offset can be approximated with a second-order polynomial that is fitted to all the data outside of the +main peak's area. Since at this point the wavelength of each data point is still unknown this is done with a rough first +estimate of the three colors' peaks' locations and widths.</p> +</div> +<div class="section" id="wavelength-and-amplitude-calibration"> +<h4>Wavelength- and amplitude calibration</h4> +<p>The photodiode's response is strongly wavelength-dependent. In particular in the blue band, the photodiode's sensitivity +gets very poor down to about 20% at the edge to ultraviolet. This effect is strong enough to move the apparent location +of the blue peak towards red.</p> +<figure> + <img src="images/photodiode_sensitivity.svg" alt="A plot of photodiode sensitivity against wavelength relative + to peak sensitivity at 820nm. The sensitivity rises from 20% at 380nm approximately linearly to 80% at 620nm, + then the rise rolls off."> + <figcaption>A plot of the photodiode's relative sensitivity in the visible spectrum. The sensitivity is + normalized against its peak at 820nm. + </figcaption> +</figure><p>The problem is that in order to remove this non-linearity, we would already have to know the wavelength of the measured +light. Since I don't, I settled for a two-step process. First, a coarse wavelength calibration is done relative to the +red peak and the short-wavelength edge of the blue peak. The photodiode measurements are then sensitivity-corrected +using this coarse measurement. Then all three channel peaks are measured in the resulting data and a fine wavelength +estimate is produced by a least-squares fit of a linear function. This fine estimate is then used for a second +sensitivity correction of all original measurements and the scale is changed from stepper motor step count to +wavelength in nanometers.</p> +<figure> + <img src="images/processed_plot_cheap_rgb.svg" alt="A plot with three wide peaks, all three of different + heights. The leftmost peak is highest at 6nA, the middle peak lowest at 1.6nA and the rightmost peak in between + at 4nA. The middle one overlaps the two on the sides. Overall, the plot spans about 300nm on its x axis with + each peak being around 100nm wide."> + <figcaption>A plot of the processed measurements. From left to right, the three peaks are blue, green and red. + </figcaption> +</figure><!-- FIXME re-do these measurements, avoiding clipping --> +<!-- FIXME re-do calibration using CCFL --> +<!-- FIXME calibration for brightness imbalance due to wedge-shaped projection of spectrum --> +</div> +<div class="section" id="color-space-mapping"> +<h4>Color space mapping</h4> +<p>Finally, to achieve the objective of measuring the LED tape's channels' precise color coordinates the measured spetra +have to be matched against the color spaces' <em>color matching functions</em>. The color matching functions describe how +strong the color space's idealized <em>standard observer</em> would react to light at a particular wavelength. Going from a +measured spectrum to color coordinates XYZ works by integrating over the product of the measurement and each color +coordinate's color matching function.</p> +<p>The result are three color coordinates X, Y and Z for each channel R, G and B yielding nine coordinates in total. When +written as a matrix conversion between XYZ color space and LED-RGB color space is as simple as multiplying that matrix +(or its inverse) and a vector from one of the color spaces.</p> +<p>In XYZ space, the set of colors that can be produced with this LED tape is described by the <a class="reference external" href="https://en.wikipedia.org/wiki/Parallelepiped">parallelepiped</a> spanned by +the three channel's XYZ vectors. In the following figures, you can see a three-dimensional model of the RGB LED's color +space (colorful) as well as sRGB (white) for comparison plotted within CIE 1931 XYZ. There is no natural map to scale +both so for this illustration the LED color space has been scaled to fit. These figures were made with blender and a few +lines of python. The blender project file including all settings and the python script to generate the color space +models can be found in the <a class="reference external" href="https://github.com/jaseg/led_drv">project repo</a>.</p> +<figure> + <video controls loop> + <source src="video/led_within_srgb_scale=1.0.mkv" type="video/h264"> + <source src="video/led_within_srgb_scale=1.0.webm" type="video/webm"> + Your browser does not support the HTML5 video tag. + </video> + <figcaption>Illustration of the measured LED color space scaled to fit within XYZ with sRGB (light gray) for + comparison. The thick, white line is the spectral locus. + + <a href="video/led_within_srgb_scale=1.0.mkv">mkv/h264 download</a> / + <a href="video/led_within_srgb_scale=1.0.webm">webm download</a> + </figcaption> +</figure><p>As you can see, the result is pretty disappointing. The LED's color space parallepiped is very narrow, which is because +the blue channel is much brighter than the other two channels. An easy fix for this is to scale-up the RGB space and +drop any values outside XYZ. The scaling factor is a trade-off between color space coverage and brightness. You can +produce the most colors when you clip all channels to brightness of the weakest channel (green in this case), but that +will make the result very dim. Scaling brightness like that stretches the RGB parallelepiped along its major axis. Up to +a point the number of possible colors (the gamut) increases at expense of maximum brightness. When the parallelepiped is +stretched far enought for all three channel vectors to be outside the 1,1,1 XYZ-cube, maximum brightness continues to +decrease but the gamut stays constant. I don't know a simple scientific way to solve this problem, so I just played +around with a couple of factors and settled on 2.5 as a reasonable compromise. Below is an illustration.</p> +<figure> + <video controls loop> + <source src="video/led_within_srgb_fancy_camera_path_scale=2.5.mkv" type="video/h264"> + <source src="video/led_within_srgb_fancy_camera_path_scale=2.5.webm" type="video/webm"> + Your browser does not support the HTML5 video tag. + </video> + <figcaption>Illustration of the measured LED color space at scale factor 2.5 within XYZ with sRGB (light gray) + for comparison. The thick, white line is the spectral locus. + + <a href="video/led_within_srgb_fancy_camera_path_scale=2.5.mkv">mkv/h264 download</a> / + <a href="video/led_within_srgb_fancy_camera_path_scale=2.5.webm">webm download</a> + </figcaption> +</figure></div> +</div> +</div> +<div class="section" id="firmware-implementation"> +<h2>Firmware implementation</h2> +<p>In the end, the above measurements yield two matrices: One for mapping XYZ to RGB, and one for mapping RGB to XYZ. Of +the several versions of CIE XYZ I chose the CIE 1931 XYZ color space as a basis for the firmware because it is most +popular. Mapping a color coordinate in one color space to the other is as simple as performing nine floating-point +multiplications and six additions. Mapping Lab or Lch to RGB is done by first mapping Lab/Lch to XYZ, then XYZ to RGB. +Lab to XYZ is somewhat complex since it requires a floating-point power for gamma correction, but any self-respecting +libc will have one of those so this is still no problem. Lch also requires floating-point sine and cosine functions, but +these should still be no problem on most hardware.</p> +<p>My implementation of these conversions in the ESP8266 firmware of my <a class="reference external" href="http://jaseg.de/blog/wifi-led-driver/">Wifi LED driver</a> can be found <a class="reference external" href="https://github.com/jaseg/esp_led_drv/blob/master/user/led_controller.c">on Github</a>. You +can view the Jupyter notebook most of the analysis above <a class="reference external" href="http://nbviewer.jupyter.org/github/jaseg/led_drv/blob/master/doc/Spectrum%20Measurement.ipynb">here</a>.</p> +</div> +</div> + </main><footer> + Copyright © 2023 Jan Sebastian Götte + / <a href="/about/">About</a> + / <a href="/imprint/">Imprint</a> +</footer> +<script> + if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser + contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to + prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition. + In support of an open web, this website does not function with this DRM. Please install a browser such + as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports + ad blockers.`; + </script> + </body> +</html> diff --git a/content/blog/led-characterization/video/led_within_srgb_fancy_camera_path_scale=2.5.mkv b/blog/led-characterization/video/led_within_srgb_fancy_camera_path_scale=2.5.mkv Binary files differindex 0a1eece..0a1eece 100644 --- a/content/blog/led-characterization/video/led_within_srgb_fancy_camera_path_scale=2.5.mkv +++ b/blog/led-characterization/video/led_within_srgb_fancy_camera_path_scale=2.5.mkv diff --git a/content/blog/led-characterization/video/led_within_srgb_fancy_camera_path_scale=2.5.webm b/blog/led-characterization/video/led_within_srgb_fancy_camera_path_scale=2.5.webm Binary files differindex 04d355c..04d355c 100644 --- a/content/blog/led-characterization/video/led_within_srgb_fancy_camera_path_scale=2.5.webm +++ b/blog/led-characterization/video/led_within_srgb_fancy_camera_path_scale=2.5.webm diff --git a/content/blog/led-characterization/video/led_within_srgb_scale=1.0.mkv b/blog/led-characterization/video/led_within_srgb_scale=1.0.mkv Binary files differindex 69dfccf..69dfccf 100644 --- a/content/blog/led-characterization/video/led_within_srgb_scale=1.0.mkv +++ b/blog/led-characterization/video/led_within_srgb_scale=1.0.mkv diff --git a/content/blog/led-characterization/video/led_within_srgb_scale=1.0.webm b/blog/led-characterization/video/led_within_srgb_scale=1.0.webm Binary files differindex 8034882..8034882 100644 --- a/content/blog/led-characterization/video/led_within_srgb_scale=1.0.webm +++ b/blog/led-characterization/video/led_within_srgb_scale=1.0.webm diff --git a/content/blog/led-characterization/video/led_within_srgb_scale=2.5.mkv b/blog/led-characterization/video/led_within_srgb_scale=2.5.mkv Binary files differindex a7fba0b..a7fba0b 100644 --- a/content/blog/led-characterization/video/led_within_srgb_scale=2.5.mkv +++ b/blog/led-characterization/video/led_within_srgb_scale=2.5.mkv diff --git a/content/blog/led-characterization/video/led_within_srgb_scale=2.5.webm b/blog/led-characterization/video/led_within_srgb_scale=2.5.webm Binary files differindex d0c9135..d0c9135 100644 --- a/content/blog/led-characterization/video/led_within_srgb_scale=2.5.webm +++ b/blog/led-characterization/video/led_within_srgb_scale=2.5.webm diff --git a/content/blog/led-characterization/video/led_within_srgb_scale=3.mkv b/blog/led-characterization/video/led_within_srgb_scale=3.mkv Binary files differindex 94c7750..94c7750 100644 --- a/content/blog/led-characterization/video/led_within_srgb_scale=3.mkv +++ b/blog/led-characterization/video/led_within_srgb_scale=3.mkv diff --git a/content/blog/led-characterization/video/led_within_srgb_scale=3.webm b/blog/led-characterization/video/led_within_srgb_scale=3.webm Binary files differindex 3dc88cc..3dc88cc 100644 --- a/content/blog/led-characterization/video/led_within_srgb_scale=3.webm +++ b/blog/led-characterization/video/led_within_srgb_scale=3.webm diff --git a/content/blog/led-characterization/video/sRGB.mkv b/blog/led-characterization/video/sRGB.mkv Binary files differindex 903c719..903c719 100644 --- a/content/blog/led-characterization/video/sRGB.mkv +++ b/blog/led-characterization/video/sRGB.mkv diff --git a/content/blog/led-characterization/video/sRGB.webm b/blog/led-characterization/video/sRGB.webm Binary files differindex 737cc1b..737cc1b 100644 --- a/content/blog/led-characterization/video/sRGB.webm +++ b/blog/led-characterization/video/sRGB.webm diff --git a/content/blog/led-characterization/video/scale=1.mkv b/blog/led-characterization/video/scale=1.mkv Binary files differindex 410896e..410896e 100644 --- a/content/blog/led-characterization/video/scale=1.mkv +++ b/blog/led-characterization/video/scale=1.mkv diff --git a/content/blog/led-characterization/video/scale=1.webm b/blog/led-characterization/video/scale=1.webm Binary files differindex dc599be..dc599be 100644 --- a/content/blog/led-characterization/video/scale=1.webm +++ b/blog/led-characterization/video/scale=1.webm diff --git a/content/blog/led-characterization/video/scale=2.5.mkv b/blog/led-characterization/video/scale=2.5.mkv Binary files differindex 6ff3619..6ff3619 100644 --- a/content/blog/led-characterization/video/scale=2.5.mkv +++ b/blog/led-characterization/video/scale=2.5.mkv diff --git a/content/blog/led-characterization/video/scale=2.5.webm b/blog/led-characterization/video/scale=2.5.webm Binary files differindex 6a6a860..6a6a860 100644 --- a/content/blog/led-characterization/video/scale=2.5.webm +++ b/blog/led-characterization/video/scale=2.5.webm diff --git a/content/blog/led-characterization/video/scale=5.mkv b/blog/led-characterization/video/scale=5.mkv Binary files differindex b4e7e65..b4e7e65 100644 --- a/content/blog/led-characterization/video/scale=5.mkv +++ b/blog/led-characterization/video/scale=5.mkv diff --git a/content/blog/led-characterization/video/scale=5.webm b/blog/led-characterization/video/scale=5.webm Binary files differindex 0298a11..0298a11 100644 --- a/content/blog/led-characterization/video/scale=5.webm +++ b/blog/led-characterization/video/scale=5.webm diff --git a/content/blog/multichannel-led-driver/images/asymmetric_iled.svg b/blog/multichannel-led-driver/images/asymmetric_iled.svg index c18d5de..c18d5de 100644 --- a/content/blog/multichannel-led-driver/images/asymmetric_iled.svg +++ b/blog/multichannel-led-driver/images/asymmetric_iled.svg diff --git a/content/blog/multichannel-led-driver/images/asymmetric_vgate.svg b/blog/multichannel-led-driver/images/asymmetric_vgate.svg index 473f494..473f494 100644 --- a/content/blog/multichannel-led-driver/images/asymmetric_vgate.svg +++ b/blog/multichannel-led-driver/images/asymmetric_vgate.svg diff --git a/content/blog/multichannel-led-driver/images/bcm_schema.jpg b/blog/multichannel-led-driver/images/bcm_schema.jpg Binary files differindex 5d3ef08..5d3ef08 100644 --- a/content/blog/multichannel-led-driver/images/bcm_schema.jpg +++ b/blog/multichannel-led-driver/images/bcm_schema.jpg diff --git a/content/blog/multichannel-led-driver/images/corrected_brightness_sim.svg b/blog/multichannel-led-driver/images/corrected_brightness_sim.svg index 2b9cf16..2b9cf16 100644 --- a/content/blog/multichannel-led-driver/images/corrected_brightness_sim.svg +++ b/blog/multichannel-led-driver/images/corrected_brightness_sim.svg diff --git a/content/blog/multichannel-led-driver/images/driver_linearity_raw.svg b/blog/multichannel-led-driver/images/driver_linearity_raw.svg index 58aa43f..58aa43f 100644 --- a/content/blog/multichannel-led-driver/images/driver_linearity_raw.svg +++ b/blog/multichannel-led-driver/images/driver_linearity_raw.svg diff --git a/content/blog/multichannel-led-driver/images/driver_output_ltspice_schematic.jpg b/blog/multichannel-led-driver/images/driver_output_ltspice_schematic.jpg Binary files differindex 52000a8..52000a8 100644 --- a/content/blog/multichannel-led-driver/images/driver_output_ltspice_schematic.jpg +++ b/blog/multichannel-led-driver/images/driver_output_ltspice_schematic.jpg diff --git a/content/blog/multichannel-led-driver/images/driver_pcb_built.jpg b/blog/multichannel-led-driver/images/driver_pcb_built.jpg Binary files differindex f5da956..f5da956 100644 --- a/content/blog/multichannel-led-driver/images/driver_pcb_built.jpg +++ b/blog/multichannel-led-driver/images/driver_pcb_built.jpg diff --git a/content/blog/multichannel-led-driver/images/driver_ringing_strong.jpg b/blog/multichannel-led-driver/images/driver_ringing_strong.jpg Binary files differindex 0419a0e..0419a0e 100644 --- a/content/blog/multichannel-led-driver/images/driver_ringing_strong.jpg +++ b/blog/multichannel-led-driver/images/driver_ringing_strong.jpg diff --git a/content/blog/multichannel-led-driver/images/driver_ringing_weak.jpg b/blog/multichannel-led-driver/images/driver_ringing_weak.jpg Binary files differindex 12f9c5d..12f9c5d 100644 --- a/content/blog/multichannel-led-driver/images/driver_ringing_weak.jpg +++ b/blog/multichannel-led-driver/images/driver_ringing_weak.jpg diff --git a/content/blog/multichannel-led-driver/images/led_strip_alight.jpg b/blog/multichannel-led-driver/images/led_strip_alight.jpg Binary files differindex b001395..b001395 100644 --- a/content/blog/multichannel-led-driver/images/led_strip_alight.jpg +++ b/blog/multichannel-led-driver/images/led_strip_alight.jpg diff --git a/content/blog/multichannel-led-driver/images/linearization_setup.jpg b/blog/multichannel-led-driver/images/linearization_setup.jpg Binary files differindex faafc92..faafc92 100644 --- a/content/blog/multichannel-led-driver/images/linearization_setup.jpg +++ b/blog/multichannel-led-driver/images/linearization_setup.jpg diff --git a/content/blog/multichannel-led-driver/images/olsndot_output_schematic.jpg b/blog/multichannel-led-driver/images/olsndot_output_schematic.jpg Binary files differindex 90941df..90941df 100644 --- a/content/blog/multichannel-led-driver/images/olsndot_output_schematic.jpg +++ b/blog/multichannel-led-driver/images/olsndot_output_schematic.jpg diff --git a/content/blog/multichannel-led-driver/images/olsndot_pcb.png b/blog/multichannel-led-driver/images/olsndot_pcb.png Binary files differindex 87b10f8..87b10f8 100644 --- a/content/blog/multichannel-led-driver/images/olsndot_pcb.png +++ b/blog/multichannel-led-driver/images/olsndot_pcb.png diff --git a/content/blog/multichannel-led-driver/images/olsndot_schematic.png b/blog/multichannel-led-driver/images/olsndot_schematic.png Binary files differindex 69906e5..69906e5 100644 --- a/content/blog/multichannel-led-driver/images/olsndot_schematic.png +++ b/blog/multichannel-led-driver/images/olsndot_schematic.png diff --git a/content/blog/multichannel-led-driver/images/overshoot_sim_r0.svg b/blog/multichannel-led-driver/images/overshoot_sim_r0.svg index 004872b..004872b 100644 --- a/content/blog/multichannel-led-driver/images/overshoot_sim_r0.svg +++ b/blog/multichannel-led-driver/images/overshoot_sim_r0.svg diff --git a/content/blog/multichannel-led-driver/images/overshoot_sim_r100.svg b/blog/multichannel-led-driver/images/overshoot_sim_r100.svg index c8efd61..c8efd61 100644 --- a/content/blog/multichannel-led-driver/images/overshoot_sim_r100.svg +++ b/blog/multichannel-led-driver/images/overshoot_sim_r100.svg diff --git a/content/blog/multichannel-led-driver/images/pwm_schema.jpg b/blog/multichannel-led-driver/images/pwm_schema.jpg Binary files differindex 0265665..0265665 100644 --- a/content/blog/multichannel-led-driver/images/pwm_schema.jpg +++ b/blog/multichannel-led-driver/images/pwm_schema.jpg diff --git a/content/blog/multichannel-led-driver/images/uncorrected_brightness_sim.svg b/blog/multichannel-led-driver/images/uncorrected_brightness_sim.svg index 28cb4be..28cb4be 100644 --- a/content/blog/multichannel-led-driver/images/uncorrected_brightness_sim.svg +++ b/blog/multichannel-led-driver/images/uncorrected_brightness_sim.svg diff --git a/blog/multichannel-led-driver/index.html b/blog/multichannel-led-driver/index.html new file mode 100644 index 0000000..3c6ed1c --- /dev/null +++ b/blog/multichannel-led-driver/index.html @@ -0,0 +1,384 @@ +<!DOCTYPE html> +<html><head> + <meta charset="utf-8"> + <title>32-Channel LED tape driver | Home</title> + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="mobile-web-app-capable" content="yes"> + <meta name="color-scheme" content="dark light"> + <link rel="stylesheet" href="/style.css"> +</head> +<body><nav> + <div class="internal"> + + <a href="/" title="Home">Home</a> + <a href="/blog/" title="Blog">Blog</a> + <a href="/projects/" title="Projects">Projects</a> + <a href="/about/" title="About">About</a> + </div> + <div class="external"> + <a href="https://git.jaseg.de/" title="cgit">cgit</a> + <a href="https://github.com/jaseg" title="Github">Github</a> + <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a> + <a href="https://chaos.social/jaseg" title="Mastodon">Mastodon</a> + </span> +</nav> + + <header> + <h1>32-Channel LED tape driver</h1> +<ul class="breadcrumbs"> + <li><a href="/">jaseg.de</a></li> + <li><a href="/blog/">Blog</a></li><li><a href="/blog/multichannel-led-driver/">32-Channel LED tape driver</a></li> +</ul> + <strong>2018-05-02</strong> + </header> + <main> + <div class="document"> + + +<div class="section" id="theoretical-basics"> +<h2>Theoretical basics</h2> +<p>Together, a friend and I outfitted the small staircase at Berlin's Chaos Computer Club with nice, shiny RGB-WW LED tape +for ambient lighting. This tape is like regular RGB tape but with an additional warm white channel, which makes for much +more natural pastels and whites. There are several variants of RGBW tape. Cheap ones have separate RGB and white LEDs, +which is fine for indirect lighting but does not work for direct lighting. Since we wanted to mount our tape in channels +at the front of the steps, we had to use the slightly more expensive variant with integrated RGBW LEDs. These are LEDs +in the 5050 (5.0mm by 5.0mm) form factor common with RGB LEDs that have a small section divided off for the white +channel. The red, green and blue LED chips sit together in the larger section covered with clear epoxy and the white +channel is made up from the usual blue LED inside a yellow phosphor in the smaller section.</p> +<p>Since we wanted to light up all of 15 steps, and for greatest visual effect we would have liked to be able to control +each step individually we had to find a way to control 60 channels of LED tape with a reasonable amount of hardware.</p> +<p>LED tape has integrated series resistors and runs off a fixed 12V or 24V constant-voltage supply. This means you don't +need a complex constant-current driver as you'd need with high-power LEDs. You can just hook up a section of LED tape +to a beefy MOSFET to control it. Traditionally, you would do <em>Pulse Width Modulation</em> (PWM) on the MOSFET's input to +control the LED tape's brightness.</p> +<div class="section" id="pulse-width-modulation"> +<h3>Pulse Width Modulation</h3> +<p><a class="reference external" href="https://en.wikipedia.org/wiki/Pulse-width_modulation">Pulse Width Modulation</a> is a technique of controlling the brightness of a load such as an LED with a digital signal. +The basic idea is that if you turn the LED on and off much too fast for anyone to notice, you can control its power by +changing how long you turn it on versus how long you leave it off.</p> +<p>PWM divides each second into a large number of periods. At the beginning of each period, you turn the LED on. After +that, you wait a certain time until you turn it off. Then, you wait for the next period to begin. The periods are always +the same length but you can set when you turn off the LED. If you turn it off right away, it's off almost all the time +and it looks like it's off to your eye. If you turn it off right at the end, it's on almost all the time and it looks +super bright to your eye. Now, if you turn it off halfway into the cycle, it's on half the time and it will look to your +eye as half as bright as before. This means that you can control the LED's brightness with only a digital signal and +good timing.</p> +<figure> + <img src="images/pwm_schema.jpg" alt="A visualization of PWM at different duty cycles."> + <figcaption>Waveforms of two PWM cycles at different duty cycles.</figcaption> +</figure><p>PWM works great if you have a dedicated PWM output on your microcontroller. It's extremely simple in both hardware and +software. Unfortunately for us, controlling 32 channels with PWM is not that easy. Cheap microcontrollers only have <a class="reference external" href="https://www.nxp.com/parametricSearch#/&c=c731_c380_c173_c161_c163&page=1">a +handful of hardware PWM outputs</a>, so we'd either have to do everything in software, bit-banging our LED modulation, or +we'd have to use a dedicated chip.</p> +<p>Doing PWM in software is both error-prone and slow. Since the maximum dynamic range of a PWM signal is limited by the +shortest duty cycle it can do, software PWM being slow means it has poor PWM resolution at maybe 8 bits at most. Poor +color resolution is not a problem if all you're doing is to fade around the <a class="reference external" href="https://en.wikipedia.org/wiki/HSL_and_HSV">HSV rainbow</a>, but for ambient lighting +where you <em>really</em> want to control the brightness down to a faint shimmer you need all the color resolution you can get.</p> +<p>If you rule out software PWM, what remains are dedicated <a class="reference external" href="http://www.ti.com/lit/ds/symlink/tlc5940.pdf">hardware PWM controllers</a>. Most of these have either of three +issues:</p> +<ul class="simple"> +<li>They're expensive</li> +<li>They don't have generous PWM resolution either (12 bits if you're lucky)</li> +<li>They're meant to drive small LEDs such as a 7-segment display directly and you can't just hook up a MOSFET to their +output</li> +</ul> +<p>This means we're stuck in a dilemma between two poor solutions if we'd want to do PWM. Luckily for us, PWM is not the +only modulation in town.</p> +</div> +<div class="section" id="binary-code-modulation"> +<h3>Binary Code Modulation</h3> +<p>PWM is the bread-and-butter of the maker crowd. Everyone and their cat is doing it and it works really well most of the +time. Unbeknownst to most of the maker crowd, there is however another popular modulation method that's mostly used in +professional LED systems: Enter <a class="reference external" href="http://www.batsocks.co.uk/readme/art_bcm_1.htm">*Binary Code Modulation* (BCM)</a>.</p> +<p>BCM is to PWM sort of what barcodes are to handwriting. While PWM is easy to understand and simple to implement if all +you have is a counter and an IO pin, BCM is more complicated. On the other hand, computers can do complicated and BCM +really shines in multi-channel applications.</p> +<p>Similar to PWM, BCM works by turning on and off the LED in short periods fast enough to make your eye perceive it as +partially on all the time. In PWM the channel's brightness is linearly dependent on its duty cycle, i.e. the percentage +it is turned on. In PWM the duty cycle D is the total period T divided by the on period T_on. The issue with doing PWM +on many channels at once is that you have to turn off each channel at the exact time to match its duty cycle. +Controlling many IO pins at once with precise timing is really hard to do in software.</p> +<p>BCM avoids this by further dividing each period into smaller periods which we'll call <em>bit periods</em> and splitting each +channel's duty cycle into chunks the size of these bit periods. The amazingly elegant thing in BCM now is that as you +can guess from the name these bit periods are weighted in powers of two. Say the shortest bit period lasts 1 +microsecond. Then the second-shortest bit period is 2 microseconds and the third is 4, the fifth 8, the sixth 16 and so +on.</p> +<figure> + <img src="images/bcm_schema.jpg" alt="A visualization of BCM at different duty cycles."> + <figcaption>Waveforms of a single 4-bit BCM cycle at different duty cycles. This BCM can produce 16 different + levels.</figcaption> +</figure><p>Staggered like this, you turn on the LED for integer value of microseconds by turning it on in the bit periods +corresponding to the binary bits of that value. If I want my LED to light for 19 microseconds every period, I turn it on +in the 16 microsecond bit period, the 2 microsecond bit period and the 1 microsecond bit period and leave it off for the +4 and 8 mircosecond bit periods.</p> +<p>Now, how this is better instead of just more complicated than plain old PWM might not be clear yet. But consider this: +Turning on and off a large number of channels, each at its own arbitrary time is hard because doing the timing in +software is hard. We can't use hardware timers since we only have two or three of those, and we have 32 channels. +However, we can use one hardware timer to trigger a really cheap external latch to turn on or off the 32 channels all at +once. With this setup, we can only controll all channels at once, but we can do so with very precise timing.</p> +<p>All we need to do is to set our timer to the durations of the BCM bit periods, and we can get the same result as we'd +get with PWM with only one hardware timer and a bit of code that is not timing-critical anymore.</p> +<div class="section" id="applications-of-binary-code-modulation"> +<h4>Applications of Binary Code Modulation</h4> +<p>BCM is a truly wondrous technique, and outside of hobbyist circles it is in fact very widely known. Though we're using +it to control just 32 channels here, you can do much more channels without any problems. The most common application +where BCM is invariably used is <em>any</em> kind of LED screen. Controlling the thousands and thousands of LEDs in an LED +screen with PWM with a dedicated timer for each LED would not be feasible. With BCM, all you need to dedicate to a +single LED is a flipflop (or part of one if you're multiplexing). In fact, there is a whole range of <a class="reference external" href="http://www.vabolis.lt/stuff/MBI5026.pdf">ICs with no other +purpose than to enable BCM on large LED matrices</a>. Basically, these are a +high-speed shift register with latched outputs much like the venerable <a class="reference external" href="http://www.ti.com/lit/ds/symlink/sn74hc595.pdf">74HC595</a>, only their outputs are constant-current +sinks made so that you can directly connect an LED to them.</p> +</div> +<div class="section" id="running-bcm-on-led-tape"> +<h4>Running BCM on LED tape</h4> +<p>In our case, we don't need any special driver chips to control our LED tape. We just connect the outputs of a <a class="reference external" href="http://www.ti.com/lit/ds/symlink/sn74hc595.pdf">74HC595</a> +shift register to one <a class="reference external" href="https://en.wikipedia.org/wiki/MOSFET">MOSFET</a> each, and then we directly connect the LED tape to these MOSFETs. The MOSFETs allow us to +drive a couple of amps into the LED tape from the weak outputs of the shift register.</p> +<p>The BCM timing is done by hooking up two timer channels of our microcontroller to the shift registers <em>strobe</em> and +<em>reset</em> inputs. We set the timer to PWM mode so we can generate pulses with precise timing. At the beginning of each +bit period, a pulse will strobe the data for this bit period that we shifted in previously. At the end of the bit +period, one pulse will reset the shift register and one will strobe the freshly-reset zeros into the outputs.</p> +<figure> + <img src="images/olsndot_output_schematic.jpg" alt="From left to right, we see the STM32, one of the shift + registers, and the LEDs and MOSFETs. The LED tape is driven to ground by the MOSFETs, which are in turn directly + driven from the shift register outputs. The shift register is wired up to the STM32 with its clock and data + inputs on SCK and MOSI and its RESET and STROBE inputs on channel 2 and 3 of timer 1."> + <figcaption> + The schematic of a single output of this LED driver. Multiple shift register stages can be cascaded. + </figcaption> +</figure><p>Our implementation of this system runs on an <a class="reference external" href="http://www.st.com/resource/en/datasheet/stm32f030f4.pdf">STM32F030F4P6</a>, the smallest, cheapest ARM microcontroller you can get from +ST. This microcontroller has only 16kB of flash and 1kB of RAM, but that's plenty for our use. We use its SPI controller +to feed the modulation data to the shift registers really fast, and we use two timer channels to control the shift +registers' reset and strobe.</p> +<p>We can easily cascade shift registers without any ill side-effects, and even hundreds of channels should be no problem +for this setup. The only reason we chose to stick to a 32-channel board is the mechanics of it. We thought it would be +easier to have several small boards instead of having one huge board with loads of connectors and cables coming off it.</p> +<p>The BOM cost per channel for our system is 3ct for a reasonable MOSFET, about 1ct for one eighth of a shift register +plus less than a cent for one resistor between shift register and MOSFET. In the end, the connectors are more expensive +than the driving circuitry.</p> +</div> +</div> +</div> +<div class="section" id="hardware-design"> +<h2>Hardware design</h2> +<p>From this starting point, we made a very prototype-y hardware design for a 32-channel 12V LED tape driver. The design is +based on the <a class="reference external" href="http://www.st.com/resource/en/datasheet/stm32f030f4.pdf">STM32F030F4P6</a> driving the shift registers as explained above. The system is controlled through an <a class="reference external" href="https://en.wikipedia.org/wiki/RS-485">RS485</a> +bus that is connected up to the microcontroller's UART using an <a class="reference external" href="https://datasheets.maximintegrated.com/en/ds/MAX1487-MAX491.pdf">MAX485</a>-compatible RS485 transceiver. The LED tape is +connected using 9-pin <a class="reference external" href="https://en.wikipedia.org/wiki/D-subminiature">SUB-D</a> connectors since they are cheap and good enough for the small current of our short segments +of LED tape. The MOSFETs we use are small <a class="reference external" href="http://www.nxp.com/documents/outline_drawing/SOT23.pdf">SOT-23</a> logic-level MOSFETs. In various prototypes we used both International +Rectifier's <a class="reference external" href="https://www.infineon.com/dgdl/?fileId=5546d462533600a4015356686fed261f">IRLML6244</a> as well as Alpha & Omega Semiconductor's <a class="reference external" href="http://aosmd.com/pdfs/datasheet/AO3400.pdf">AO3400</a>. Both are good up to about 30V/5A. Since we're +only driving about 2m of LED tape per channel we're not going above about 0.5A and the MOSFETs don't even get warm.</p> +<div class="section" id="switching-nonlinearities"> +<h3>Switching nonlinearities</h3> +<p>During testing of our initial prototype, we noticed that the brightness seemed to jump around when fading to very low +values. It turned out that our extremely simple LED driving circuit consisting of only the shift register directly +driving a MOSFET, which in turn directly drives the LED tape was maybe a little bit too simple. After some measurements +it turned out that we were looking at about 6Vpp of ringing on the driver's output voltage. The picture below is the +voltrage we saw on our oscilloscope on the LED tape.</p> +<figure> + <img src="images/driver_ringing_strong.jpg" alt="Strong ringing on the LED voltage waveform edge at about + 100% overshoot during about 70% of the cycle time."> + <figcaption>Bad ringing on the LED output voltage caused by wiring inductance. Note that the effect on the + actual LED current is less bad than this looks since the LED's V/I curve is nonlinear.</figcaption> +</figure><div class="section" id="dynamic-switching-behavior-cause-and-effect"> +<h4>Dynamic switching behavior: Cause and Effect</h4> +<p>A bit of <a class="reference external" href="http://www.analog.com/en/design-center/design-tools-and-calculators/ltspice-simulator.html">LTSpice</a> action later we found that the inductance of the few metres of cable leading to the LED tape is the +likely culprit. The figure below is the schematic used for the simulations.</p> +<figure> + <img src="images/driver_output_ltspice_schematic.jpg" alt="The LTSpice schematic of one output of the driver, + taking into account the shift register's output ESR and the wiring ESL."> + <figcaption>The schematic of the simulation in LTSpice</figcaption> +</figure><p>As tested, the driver does not include any per-output smoothing so the ~.5A transient on each BCM cycle hits the cable +in full. Combined with the cable inductance, this works out to a considerable lag of the rising edge of the LED +current, and bad ringing on its falling edge. Below is the voltage on the LED output from an LTSpice simulation of our +driver.</p> +<figure> + <img src="images/overshoot_sim_r0.svg" alt="The result of the LTSpice simulation of our driver output. The LED + current shows similar ringing to what we measured using the oscilloscope. Interestingly, the gate voltage shows + strong ringing, too."> + <figcaption>The result of our LTSpice simulation. This simulation assumes 1µH of wiring inductance and 50Ω of + output impedance on the part of the shift register. The ringing at the gate visible in the gate voltage graph is + due to feed-through of the ringing at the output through the MOSFET's parasitic Cgd.</figcaption> +</figure><p>We were able to reduce the rining and limit the effect somewhat by putting a 220Ω series resistor in between the shift +register output and the MOSFET gate. This resistor forms an RC circuit with the MOSFET's nanofarad or two of gate +capacitance. The result of this is that the LED current passing the wire's ESL rises slightly more slowly and thus the +series inductance gets excited slightly less, and the overshoot decreases. Below is a picture of the waveform with the +damping resistor in place and a picture of our measurement for comparison. The resistor values don't agree perfectly +since the estimated ESL and stray capacitance of the wiring is probably way off.</p> +<figure> + <img src="images/driver_ringing_weak.jpg" alt="Weak ringing on the LED voltage waveform edge at about 30% + overshoot during about 20% of the cycle time."> + <figcaption>Adding a resistor in front of the MOSFET gate to slow the transition damped the ringing somewhat, + but ultimately it cannot be eliminated entirely. Note how you can actually see the miller plateau on the + trailing edge of this signal. + </figcaption> +</figure><figure> + <img src="images/overshoot_sim_r100.svg" alt="The result of the LTSpice simulation of our driver output with an + extra 100 Ohms between shift register output and MOSFET gate. Similar to the oscilloscope measurement the + ringing is much reduced in its amplitude."> + <figcaption>The LTSpice simulation result with the same parameters as above but with an extra 100Ω between the + shfit register's output and the MOSFET's gate.</figcaption> +</figure><p>A side effect of this fix is that now the effective on-time of the LED tape is much longer than the duty cycle at the +shift register's output at very small duty cycles (1µs or less). This is caused by the MOSFET's <a class="reference external" href="https://www.vishay.com/docs/68214/turnonprocess.pdf">miller +plateau</a>. For illustration, below is a graph of both the excitation waveform (the boxy line) and the resulting LED +current (the other ones) both without damping (top) and with 220Ω damping (bottom). As you can see the effective duty +cycle of the LED current is not at all equal to the 50% duty cycle of the excitation square wave.</p> +<figure> + <img src="images/asymmetric_iled.svg" alt="The result of an LTSpice simulation of the LED duty cycle without and + with damping. Dampening widens the LED current waveform from 50% duty cycle with sharp edges to about 80% duty + cycle with soft edges."> + <figcaption>Simulated LED duty cycle with and without damping. The damping resistance used in this simulation + was 220Ω.</figcaption> +</figure><figure> + <img src="images/asymmetric_vgate.svg" alt="The gate voltages in the spice simulation above. The undamped + response shows sharp edges with the miller plateau being a barely noticeable step, but with strong ringing on + the trailing edge. The damped response shows RC-like slow-edges, but has wide miller plateaus on both edges + adding up to about 50% of the pulse width."> + <figcaption>The MOSFET gate voltage from the simulation in the figure above. You can clearly see how the miller + plateau (the horizontal part of the trace at about 1V) is getting much wider with added damped, and how the + resulting gate charge/discharge curve is not at all that of a capacitor anymore.</figcaption> +</figure><p>In conclusion, we have three major causes for our calculated LED brightness not matching reality:</p> +<ul class="simple"> +<li>Ringing of the equivalent series inductance of the wiring leading up to the LED tape</li> +<li>Miller plateau lag</li> +<li>The damping resistor and the MOSFET gate forming an RC filter that helps with wire ESL ringing but worsens the miller +plateau issue and deforms the LED current edges.</li> +</ul> +<p>Added up, these three effects yield a picture that agrees well with our simulations and measurements. The overall effect +is neglegible at long period durations (>10µs), but gets really bad at short period durations (<1µs). The effect is +non-linear, so correcting for it is not as simple as adding an offset.</p> +</div> +<div class="section" id="measuring-led-tape-brightness"> +<h4>Measuring LED tape brightness</h4> +<p>In order to correct for the nonlinearities mentioned above, we decided to implement a lookup table mapping BCM period to +actual timer setting. That is, each row of the table contains the actual period length we need to set the +microcontroller's timer to in order to get our intended brightness steps.</p> +<p>To calibrate our driver, we needed a setup for reproducible measurement of the relative brightness of our LED tape at +different settings. Absolute brightness is not of interest to us as the eye can't perceive it. To perform the +calibration, the LED driver is set to enable each single BCM period in turn, i.e. brightness values 1, 2, 4, 8, 16 etc.</p> +<p>The setup we used to measure the LED tape's brightness consists of a bunch of LED tape stuck into a tin can for +shielding against both stray light and electromagnetic interference and a photodiode looking at the LED tape. We used +the venerable <a class="reference external" href="http://www.vishay.com/docs/81521/bpw34.pdf">BPW34</a> photodiode in our setup as I had a bunch leftover from another project and because they are quite +sensitive owing to their physically large die area.</p> +<figure> + <img src="images/linearization_setup.jpg" alt="The led measurement setup consists of several PCBs and a + breadboard linked with a bunch of wires and a big tin can to shield the LEDs and the photodiode. A large sub-D + connector is put into the top of the tin can as a feed-through for the LED tape's control signals and the + photodiode signal. In the background the control laptop is visible."> + <figcaption>The LED brighness measurement setup. The big tin can contains a bunch of LED tape and the + photodiode. The breadboard on the right is used for the photodiode preamplifier and for jumpering around the LED + tape's channels. The red board next to it is the buspirate used as ADC. The board on the bottom left is a + TTL-to-RS485 converter and the board in the middle is the unit under test.</figcaption> +</figure><p>The photodiode's photocurrent is converted into a voltage using a very simple transimpedance amplifier based around a +<a class="reference external" href="http://ww1.microchip.com/downloads/en/DeviceDoc/21733j.pdf">MCP6002</a> opamp that was damped into oblivion with a couple nanofarads of capacitance in its feedback loop. The <a class="reference external" href="http://ww1.microchip.com/downloads/en/DeviceDoc/21733j.pdf">MCP6002</a> +is a fine choice here since I had a bunch and because it is a CMOS opamp, meaning it has low bias current that would +mess up our measurements. For many applications, opamp bias current is not a big issue but when using the opamp to +directly measure very small currents at its input it quickly swamps out the signal for most BJT-input types.</p> +<p>The transimpedance amplifier's output is read from the computer using the ADC input of a buspirate USB thinggamajob. In +general I would not recommend the buspirate as a tool for this job since it's ADC is not particularly good and it's +programming interface is positively atrocious, but it was what I had and it beat first wiring up one of the dedicated +ADC chips I had in my parts bin.</p> +<p>The computer runs a small python script cycling the LED tape through all its BCM period settings and taking a brightness +measurement at each step. Later on, these measurements can be plotted to visualize the resulting slope's linearity, and +we can even do a simulation of the resulting brightness for all possible control values by just adding the measured +photocurrents for a certain BCM setpoint just as our retinas would do.</p> +<figure> + <img src="images/driver_linearity_raw.svg" alt=""> + <figcaption> + A plot of the measured brightness of our LED tape for each BCM period. The brightness values are normalized + to the value measured at the LSB setpoint (brightness=1/65535). Ideally, this plot would show a straight + line with slope 1. Obviously, it doesn't. The bend in the curve is caused by the above-mentioned duty cycle + offset adding an offset to all brightness values. Shown is both the raw data (light), which has essentially zero + measurement error and a linear fit (dark). + + The plot is in log-log to approximate how the human eye would perceive brightness, i.e. highly sensitive at + low values but not very sensitive at all at large values. + </figcaption> +</figure><p>While it would be possible to fully automate the optimization of BCM driver lookup tables, we needed only one and in the +end I just sat down and manually tweaked the ideal values we initially calculated until I liked the result. You can see +the resulting brightness curve below.</p> +<div class="subfigure"> + <figure> + <img src="images/uncorrected_brightness_sim.svg" alt=""> + <figcaption> + Calculated brightness curve for the uncorrected BCM setup. As you can see, at low setpoints the result + is about as smooth as sandpaper, which is well in line with our observations. At high setpoints the + offset gets swamped out and the nonlinearity in the low bits is not visible anymore. + </figcaption> + </figure> + <figure> + <img src="images/corrected_brightness_sim.svg" alt=""> + <figcaption> + Brightness curve for the corrected BCM setup extrapolated using actual measurements. Looks as buttery + smooth in real life as it does in this plot. + </figcaption> + </figcaption> + </figure> +</div></div> +</div> +<div class="section" id="controlling-the-driver"> +<h3>Controlling the driver</h3> +<p>Now that our driver was behaving linear enough that you couldn't see it actually wasn't we needed a nice way to control +it from a computer of our choice. In the ultimate application (our staircase) we'll use a raspberry pi for this. Since +we already settled on an <a class="reference external" href="https://en.wikipedia.org/wiki/RS-485">RS485</a> bus for its robustness and simplicity, we had to device a protocol to control the driver +over this bus. Here, we settled on a simple, <a class="reference external" href="https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing">COBS</a>-based protocol for the reasons I wrote about in <a class="reference external" href="serial-protocols">How to talk to your +microcontroller over serial</a>.</p> +<p>To address our driver nodes, we modified the Makefile to build a random 32-bit MAC into each firmware image. The +protocol has only five message types:</p> +<ol class="arabic simple"> +<li>A 0-byte <em>ping</em> packet, to which each node would reply with its own address in the +first 100ms after boot. This can be used to initially discover the addresses of all nodes connected to the bus. You'd +spam the bus with <em>ping</em> packets, and then hit reset on each node in turn. The control computer would then receive +each device's MAC address as you hit reset.</li> +<li>A 4-byte <em>address</em> packet that says which device that the following packet is for. This way of us using the packet +length instead of a packet type field is not particularly elegant, but our system is simple enough and it was easy to +implement.</li> +<li>A 64-byte <em>frame buffer</em> packet that contains 16 bits of left-aligned brightness data for every channel</li> +<li>A one-byte <em>get status</em> packet that tells the device to respond with...</li> +<li>...a 27-byte status packet containing a brief description of the firmware (version number, channel count, bit depth +etc.) as well as the device's current life stats (VCC, temperature, uptime, UART frame errors etc.).</li> +</ol> +<p>Wrapped up in a nice python interface we can now easily enumerate any drivers we connect to a bus, query their status +and control their outputs.</p> +</div> +<div class="section" id="conclusion"> +<h3>Conclusion</h3> +<div class="subfigure"> + <figure> + <a href="images/olsndot_schematic.png"> + <img src="images/olsndot_schematic.png" alt="A picture of the LED driver schematic"> + </a> + <figcaption>The LED driver <a href="images/olsndot_schematic.png">schematic</a></figcaption> + </figure> + <figure> + <a href="images/olsndot_pcb.png"> + <img src="images/olsndot_pcb.png" alt="A picture of the LED driver PCB layout"> + </a> + <figcaption>The LED driver <a href="images/olsndot_pcb.png">PCB layout</a></figcaption> + </figure> +</div><p>Putting some thought into the control circuitry and software, you can easily control large numbers of channels of LEDs +using extremely inexpensive driving hardware without any compromises on dynamic range. The design we settled on can +drive 32 channels of LED tape with a dynamic range of 14bit at a BOM cost of below 10€. All it really takes is a couple +of shift registers and a mildly bored STM32 microcontroller.</p> +<p>Get a PDF file of the schematic and PCB layout <a class="reference external" href="olsndot_v02_schematics_and_pcb.pdf">here</a> or download the CAD files +and the firmware sources <a class="reference external" href="https://github.com/jaseg/led_drv">from github</a>. You can view the Jupyter notebook used to +analyze the brightness measurement data <a class="reference external" href="http://nbviewer.jupyter.org/github/jaseg/led_drv/blob/master/doc/Run_analysis.ipynb">here</a>.</p> +</div> +</div> +</div> + </main><footer> + Copyright © 2023 Jan Sebastian Götte + / <a href="/about/">About</a> + / <a href="/imprint/">Imprint</a> +</footer> +<script> + if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser + contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to + prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition. + In support of an open web, this website does not function with this DRM. Please install a browser such + as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports + ad blockers.`; + </script> + </body> +</html> diff --git a/content/blog/multichannel-led-driver/olsndot_v02_schematics_and_pcb.pdf b/blog/multichannel-led-driver/olsndot_v02_schematics_and_pcb.pdf Binary files differindex 2a4e037..2a4e037 100644 --- a/content/blog/multichannel-led-driver/olsndot_v02_schematics_and_pcb.pdf +++ b/blog/multichannel-led-driver/olsndot_v02_schematics_and_pcb.pdf diff --git a/blog/private-contact-discovery/index.html b/blog/private-contact-discovery/index.html new file mode 100644 index 0000000..50058d6 --- /dev/null +++ b/blog/private-contact-discovery/index.html @@ -0,0 +1,80 @@ +<!DOCTYPE html> +<html><head> + <meta charset="utf-8"> + <title>Private Contact Discovery | Home</title> + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="mobile-web-app-capable" content="yes"> + <meta name="color-scheme" content="dark light"> + <link rel="stylesheet" href="/style.css"> +</head> +<body><nav> + <div class="internal"> + + <a href="/" title="Home">Home</a> + <a href="/blog/" title="Blog">Blog</a> + <a href="/projects/" title="Projects">Projects</a> + <a href="/about/" title="About">About</a> + </div> + <div class="external"> + <a href="https://git.jaseg.de/" title="cgit">cgit</a> + <a href="https://github.com/jaseg" title="Github">Github</a> + <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a> + <a href="https://chaos.social/jaseg" title="Mastodon">Mastodon</a> + </span> +</nav> + + <header> + <h1>Private Contact Discovery</h1> +<ul class="breadcrumbs"> + <li><a href="/">jaseg.de</a></li> + <li><a href="/blog/">Blog</a></li><li><a href="/blog/private-contact-discovery/">Private Contact Discovery</a></li> +</ul> + <strong>2019-06-22</strong> + </header> + <main> + <div class="document" id="private-contact-discovery"> +<h1 class="title">Private Contact Discovery</h1> + +<p>Private Contact Discovery (PCD) is the formal name for the problem modern smartphone messenger applications have on +installation: Given a user's address book, find out which of their contacts also use the same messenger without the +messenger's servers learning anything about the user's address book. The widespread non-private way to do this is to +simply upload the user's address book to the app's operator's servers and do an SQL JOIN keyed on the phone number field +against the database of registered users. People have tried sprinkling some hashes over these phone numbers in an +attempt to improve privacy, but obviously running a brute-force preimage attack given a domain of maybe a few billion +valid inputs is not cryptographically hard.</p> +<p>Private Contact Discovery can be phrased in terms of Private Set Intersection (PSI), the cryptographic problem of having +two parties holding one set each find the intersection of their sets without disclosing any other information. PSI has +been an active field of research for a while and already yielded useful results for some use cases. Alas, none of those +results were truly practical yet for usage in PCD in a typical messenger application. They would require too much CPU +time or too much data to be transferred.</p> +<p>At USENIX Security 2019, Researchers from technical universities Graz and Darmstadt published a paper titled <em>Private +Contact Discovery at Scale</em> +(<a class="reference external" href="https://eprint.iacr.org/2019/517">eprint</a> | <a class="reference external" href="https://eprint.iacr.org/2019/517.pdf">PDF</a>). +In this paper, they basically optimize the hell out of existing cryptographic solutions to private contact discovery, +jumping from a still-impractical state of the art right to practicality. Their scheme allows a client with 1k contacts +to run PCD against a server with 1B contacts in about 3s on a phone. The main disadvantage of their scheme is that it +requires the client to in advance download a compressed database of all users, that clocks in at about 1GB for 1B users.</p> +<p>I found this paper very interesting for its immediate practical applicability. As an excuse to dig into the topic some +more, I gave a short presentation at my university lab's research seminar on this paper +(slides: <a class="reference external" href="mori_semi_psi_talk.pdf">PDF</a> | <a class="reference external" href="mori_semi_psi_talk.odp">ODP</a>).</p> +<p>Even if you're not working on secure communication systems on a day-to-day basis this paper might interest you. If +you're working with social account information of any kind I can highly recommend giving it a look. Not only might your +users benefit from improved privacy, but your company might be able to avoid a bunch of data protection and +accountability issues by simply not producing as much sensitive data in the first place.</p> +</div> + </main><footer> + Copyright © 2023 Jan Sebastian Götte + / <a href="/about/">About</a> + / <a href="/imprint/">Imprint</a> +</footer> +<script> + if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser + contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to + prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition. + In support of an open web, this website does not function with this DRM. Please install a browser such + as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports + ad blockers.`; + </script> + </body> +</html> diff --git a/content/blog/private-contact-discovery/mori_semi_psi_talk.odp b/blog/private-contact-discovery/mori_semi_psi_talk.odp Binary files differindex e7df32e..e7df32e 100644 --- a/content/blog/private-contact-discovery/mori_semi_psi_talk.odp +++ b/blog/private-contact-discovery/mori_semi_psi_talk.odp diff --git a/content/blog/private-contact-discovery/mori_semi_psi_talk.pdf b/blog/private-contact-discovery/mori_semi_psi_talk.pdf Binary files differindex e06fd63..e06fd63 100644 --- a/content/blog/private-contact-discovery/mori_semi_psi_talk.pdf +++ b/blog/private-contact-discovery/mori_semi_psi_talk.pdf diff --git a/blog/serial-protocols/index.html b/blog/serial-protocols/index.html new file mode 100644 index 0000000..e97baa5 --- /dev/null +++ b/blog/serial-protocols/index.html @@ -0,0 +1,251 @@ +<!DOCTYPE html> +<html><head> + <meta charset="utf-8"> + <title>How to talk to your microcontroller over serial | Home</title> + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="mobile-web-app-capable" content="yes"> + <meta name="color-scheme" content="dark light"> + <link rel="stylesheet" href="/style.css"> +</head> +<body><nav> + <div class="internal"> + + <a href="/" title="Home">Home</a> + <a href="/blog/" title="Blog">Blog</a> + <a href="/projects/" title="Projects">Projects</a> + <a href="/about/" title="About">About</a> + </div> + <div class="external"> + <a href="https://git.jaseg.de/" title="cgit">cgit</a> + <a href="https://github.com/jaseg" title="Github">Github</a> + <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a> + <a href="https://chaos.social/jaseg" title="Mastodon">Mastodon</a> + </span> +</nav> + + <header> + <h1>How to talk to your microcontroller over serial</h1> +<ul class="breadcrumbs"> + <li><a href="/">jaseg.de</a></li> + <li><a href="/blog/">Blog</a></li><li><a href="/blog/serial-protocols/">How to talk to your microcontroller over serial</a></li> +</ul> + <strong>2018-05-19</strong> + </header> + <main> + <div class="document"> + + +<p>Scroll to the end for the <a class="reference internal" href="#conclusion">TL;DR</a>.</p> +<p>In this article I will give an overview on the protocols spoken on serial ports, highlighting common pitfalls. I will +summarize some points on how to design a serial protocol that is simple to implement and works reliably even under error +conditions.</p> +<p>If you have done low-level microcontroller firmware you will regularly have had to stuff some data up a serial port to +another microcontroller or to a computer. In the age of USB, an old-school serial port is still the simplest and +quickest way to get communication to a control computer up and running. Integrating a ten thousand-line USB stack into +your firmware and writing the necessary low-level drivers on the host side might take days. Poking a few registers to +set up your UART to talk to an external hardware USB to serial converter is a matter of minutes.</p> +<p>This simplicity is treacherous, though. Oftentimes, you start writing your serial protocol as needs arise. Things might +start harmless with something like <tt class="docutils literal">SET_LED ON\n</tt>, but as the code grows it is easy to end up in a hot mess of command +modes, protocol states that breaks under stress. The ways in which serial protocols break are manifold. The simplest one +is that at some point a character is mangled, leading to both ends of the conversation ending up in misaligned protocol +states. With a fragile protocol, you might end up in a state that is hard to recover from. In extreme cases, this leads +to code such as <a class="reference external" href="https://github.com/juhasch/pyBusPirateLite/blob/dece35f6e421d4f6a007d1db98d148e2f2126ebb/pyBusPirateLite/base.py#L113">this gem</a> performing some sort of arcane ritual to get back to some known state, and all just because +someone did not do their homework. Below we'll embark on a journey through the lands of protocol design, exploring the +facets of this deceptively simple problem.</p> +<div class="section" id="text-based-serial-protocols"> +<h2>Text-based serial protocols</h2> +<p>The first serial protocol you've likely written is a human-readable, text-based one. Text-based protocols have the big +advantage that you can just print them on a terminal and you can immediately see what's happening. In most cases you can +even type out the protocol with your bare hands, meaning that you don't really need a debugging tool beyond a serial +console.</p> +<p>However, text-based protocols also have a number of disadvantages. Depending on your application, these might not matter +and in many cases a text-based protocol is the most sensible solution. But then, in some cases they might and it's good +to know when you hit one of them.</p> +<div class="section" id="problems"> +<h3>Problems</h3> +<div class="section" id="low-information-density"> +<h4>Low information density</h4> +<p>Generally, you won't be able to stuff much more than four or five bit of information down a serial port using a +single byte of a human-readable protocol. In many cases you will get much less. If you have 10 commands that are only +issued a couple times a second nobody cares that you spend maybe ten bytes per command on nice, verbose strings such as +<tt class="docutils literal">SET LED</tt>. But if you're trying to squeeze a half-kilobyte framebuffer down the line you might start to notice the +difference between hex and base-64 encoding, and a binary protocol might really be more up to the job.</p> +</div> +<div class="section" id="complex-parsing-code"> +<h4>Complex parsing code</h4> +<p>On the computer side of thing, with the whole phalanx of an operating system, the standard library of your programming +language of choice and for all intents and purposes unlimted CPU and memory resources to spare you can easily parse +anything spoken on a serial port in real time, even at a blazing fast full Megabaud. The microcontroller side however is +an entirely different beast. On a small microcontroller, <a class="reference external" href="http://git.musl-libc.org/cgit/musl/tree/src/stdio/vfprintf.c">printf</a> alone will eat about half your flash. On most small +microcontrollers, you just won't get a regex library even though it would make parsing textual commands <em>so much +simpler</em>. Lacking these resources, you might end up hand-knitting a lot of low-level C code to do something seemingly +simple such as parsing <tt class="docutils literal">set_channel (13, <span class="pre">1.1333)\n</span></tt>. These issues have to be taken into account in the protocol design +from the beginning. For example, you don't really need matching parentheses, don't use them.</p> +</div> +<div class="section" id="fragile-protocol-state"> +<h4>Fragile protocol state</h4> +<p>Say you have a <tt class="docutils literal">SET_DISPLAY</tt> command. Now say your display can display four lines of text. The obvious approach to this +is probably the <a class="reference external" href="https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol">SMTP</a> or <a class="reference external" href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol">HTTP</a> way of sending <tt class="docutils literal">SET_DISPLAY\nThis is line 1\nThis is line 2\n\n</tt>. This would certainly +work, but it is very fragile. With this protocol, you're in trouble if at any point the terminating second newline +character gets mangled (say, someone unplugs the cable, or the control computer reboots, or a cosmic ray hits something +and <tt class="docutils literal">0x10 '\n'</tt> turns into <tt class="docutils literal">0x50 'P'</tt>).</p> +</div> +<div class="section" id="timeouts-don-t-work"> +<h4>Timeouts don't work</h4> +<p>You might try to solve the problem of your protocol state machine tangling up with a timeout. "If I don't get a valid +command for more than 200ms I go back to default state." But consider the above example. Say, your control computer +sends a <tt class="docutils literal">SET_DISPLAY</tt> command every 100ms. If in one of them the state machine tangles up, the parser hangs since the +timeout is never hit, because a new line of text is arriving every 100ms.</p> +</div> +<div class="section" id="framing-is-hard"> +<h4>Framing is hard</h4> +<p>You might also try to drop the second newline and using a convention such as <tt class="docutils literal">SET_DISPLAY</tt> is followed by two lines of +text, then commands resume.". This works as long as your display contents never look like commands. If you are only ever +displaying the same three messages on a character LCD that might work, but if you're displaying binary framebuffer +data you've lost.</p> +</div> +</div> +<div class="section" id="solutions"> +<h3>Solutions</h3> +<div class="section" id="keep-the-state-machine-simple"> +<h4>Keep the state machine simple</h4> +<p>In a text-based protocol, always use a single line of text to represent a single command. Don't do protocol states or +modes where you can toggle between different interpretations for a line. If you have to send human-readable text as part +of a command (such as <tt class="docutils literal">SET_DISPLAY</tt>), escape it so it doesn't contain any newlines.</p> +<p>This way, you keep your protocol state machine simple. If at any time your serial trips and flips a bit or looses a byte +your protocol will recover on the next newline character, returning to its base state.</p> +</div> +<div class="section" id="encode-numbers-in-hex-when-possible"> +<h4>Encode numbers in hex when possible</h4> +<p>Printing a number in hexadecimal is a very tidy operation, even on the smalest 8-bit microcontrollers. In contrast, +printing decimal requires both division and remainder in a loop which might get annoyingly code- and time-intensive on +large numbers (say a 32-bit int) and small microcontrollers.</p> +<p>If you have to send fractional values, consider their precision. Instead of sending a 12 bit ADC result as a 32-bit +float formatted like <tt class="docutils literal">0.176513671875</tt> sending <tt class="docutils literal">0x2d3</tt> and dividing by 4096 on the host might be more sensible. If you +really have to communicate big floats and you can't take the overhead of including both <a class="reference external" href="http://git.musl-libc.org/cgit/musl/tree/src/stdio/vfprintf.c">printf</a> and <a class="reference external" href="http://git.musl-libc.org/cgit/musl/tree/src/stdio/vfscanf.c">scanf</a> you can +use hexadecimal floating point, which is basically <tt class="docutils literal"><span class="pre">hex((int)foo)</span> + "." + <span class="pre">hex((int)(65536*(foo</span> - <span class="pre">(int)foo)))</span></tt> for four +digits. You can also just hex-encode the binary <a class="reference external" href="https://en.wikipedia.org/wiki/IEEE_754">IEEE-754</a> representation of the float, sending <tt class="docutils literal"><span class="pre">hex(*(int</span> <span class="pre">*)&float)</span></tt>. +Most programming languages will have a <a class="reference external" href="https://docs.python.org/3.5/library/struct.html">simple, built-in means to parse this sort of thing</a>.</p> +</div> +<div class="section" id="escape-multiline-strings"> +<h4>Escape multiline strings</h4> +<p>If you have to send arbitrary strings, escape special characters. This not only has the advantage of yielding a robust +protocol: It also ensures you can actually see everything that's going on when debugging. The string <tt class="docutils literal">"\r\n"</tt> is easy to +distinguish from <tt class="docutils literal">"\n"</tt> while your terminal emulator might not care.</p> +<p>The simplest encoding to use is the C-style backslash encoding. Host-side, most languages will have a <a class="reference external" href="https://docs.python.org/3.5/library/codecs.html#text-encodings">built-in means of +escaping a string like that</a>.</p> +</div> +</div> +<div class="section" id="encoding-binary-data"> +<h3>Encoding binary data</h3> +<p>For binary data, hex and base-64 are the most common encodings. Since hex is simpler to implement I'd go with it unless +I really need the 30% bandwidth improvement base-64 brings.</p> +</div> +</div> +<div class="section" id="binary-serial-protocols"> +<h2>Binary serial protocols</h2> +<p>In contrast to anything human-readable, binary protocols are generally more bandwidth-efficient and are easier to format +and parse. However, binary protocols come with their own version of the caveats we discussed for text-based protocols.</p> +<div class="section" id="the-framing-problem-in-binary-protocols"> +<h3>The framing problem in binary protocols</h3> +<p>The most basic problems with binary protocols as with text-based ones is framing, i.e. splitting up the continuous +serial data stream into discrete packets. The issue is that it is that you have to somehow mark boundaries between +frames. The simplest way would be to use some special character to delimit frames, but then any 8-bit character you +could choose could also occur within a frame.</p> +<div class="section" id="slip-ppp-like-special-character-framing"> +<h4>SLIP/PPP-like special character framing</h4> +<p>Some protocols solve this problem much like we have solved it above for strings in line-based protocols, by escaping any +occurence of the special delimiter character within frames. That is, if you want to use <tt class="docutils literal">0x00</tt> as a delimiter, you would +encode a packet containing <tt class="docutils literal">0xde 0xad 0x00 0xbe 0xef</tt> as something like <tt class="docutils literal">0xde 0xad 0x01 0x02 0xbe 0xef</tt>, replacing the +null byte with a magic sequence. This framing works, but is has one critical disadvantage: The length of the resulting +escaped data is dependent on the raw data, and in the worst case twice as long. In a raw packet consisting entirely of +null bytes, every byte must be escaped with two escape bytes. This means that in this case the packet length doubles, +and in this particular case we're even less efficient than base-64.</p> +<p>Highly variable packet length is also bad since it makes it very hard to make any timing guarantees for our protocol.</p> +</div> +<div class="section" id="bit-framing"> +<h4>9-bit framing</h4> +<p>A framing mode sometimes used is to configure the UARTs to transmit 9-bit characters and to use the 9th bit to designate +control characters. This works really well, and gives plenty of control characters to work with. The main problem with +this is that a 9-bit serial interface is highly nonstandard and you need UARTs on both ends that actually support this +mode. Another issue is that though more efficient than both delmitier-based and purely text-based protocols, it still +incurs an extra about 10% of bandwidth overhead. This is not a lot if all you're sending is a little command every now +and then, but if you're trying to push large amounts of data through your serial it's still bad.</p> +</div> +<div class="section" id="cobs"> +<h4>COBS</h4> +<p>Given the limitations of the two above-mentioned framing formats, we really want something better. The <a class="reference external" href="https://en.wikipedia.org/wiki/Serial_Line_Internet_Protocol">Serial Line +Internet Protocol (SLIP)</a> as well as the <a class="reference external" href="https://en.wikipedia.org/wiki/Point-to-Point_Protocol">Point to Point Protocol (PPP)</a>, standardized in 1988 and 1994 respectively, +both use escape sequences. This might come as a surprise, but humanity has actually still made significant technological +progress on protocols for 8-bit serial interfaces until the turn of the millennium. In 1999, <a class="reference external" href="http://www.stuartcheshire.org/papers/COBSforToN.pdf">Consistent Overhead Byte +Stuffing (COBS)</a> (<a class="reference external" href="https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing">wiki</a>) was published by a few +researchers from Apple Computer and Stanford University. As a reaction on the bandwidth doubling problem present in +<a class="reference external" href="https://en.wikipedia.org/wiki/Point-to-Point_Protocol">PPP</a>, COBS <em>always</em> has an overhead of a single byte, no matter what or how long a packet's content is.</p> +<p>COBS uses the null byte as a delimiter interleaves all the raw packet data and a <a class="reference external" href="https://en.wikipedia.org/wiki/Run-length_encoding">run-length encoding</a> of the non-zero +portions of the raw packet. That is, it prepends the number of bytes until the first zero byte to the packet, plus one. +Then it takes all the leading non-zero bytes of the packet, unmodified. Then, it again encodes the distance from the +first zero to the second zero, plus one. And then it takes the second non-zero run of bytes unmodified. And so on. At +the end, the packet is terminated with a zero byte.</p> +<p>The result of this scheme is that the encoded packet does not contain any zero bytes, as every zero byte has been +replaced with the number of bytes until the next zero byte, plus one, and that can't be zero. Both formatter and parser +each have to keep a counter running to keep track of the distances between zero bytes. The first byte of the packet +initializes that counter and is dropped by the parser. After that, every encoded byte received results in one raw byte +parsed.</p> +<p>While this might sound more complicated than the escaping explained above, the gains in predictability and efficiency +are worth it. An implementation of encoder and decoder should each be about ten lines of C or two lines of Python. A +minor asymmetry of the protocol is that while decoding can be done in-place, encoding either needs two passes or you +need to scan forward for the next null byte.</p> +</div> +</div> +<div class="section" id="state-machines-and-error-recovery"> +<h3>State machines and error recovery</h3> +<p>In binary protocols even more than in textual ones it is tempting to build complex state machines triggering actions on +a sequence of protocol packets. Please resist that temptation. As with textual protocols keeping the protocol state to +the minimum possible allows for a self-synchronizing protocol. A serial protocol should be designed such that if due to +a dropped packet or two both ends will naturally re-synchronize within another packet or two. A simple way of doing that +is to always transmit one semantic command per packet and to design these commands in the most <a class="reference external" href="https://en.wikipedia.org/wiki/Idempotence#Computer_science_meaning">idempotent</a> way possible. +For example, when filling a framebuffer piece by piece, include the offset in each piece instead of keeping track of it +on the receiving side.</p> +</div> +</div> +<div class="section" id="conclusion"> +<h2>Conclusion</h2> +<p>Here's your five-step guide to serial bliss:</p> +<ol class="arabic simple"> +<li>Unless you have super-special requirements, always use the slowest you can get away with from 9600Bd, 115200Bd or +1MBd. 8N1 framing if you're talking to anything but another microcontroller on the same board. Using common values +like these makes it easier when you'll inevitably have to guess these at some point in the future ;)</li> +<li>If you're doing something simple and speed is not a particular concern, use a human-readable text-based protocol. Use +one command/reply per line, begin each line with some sort of command word and format numbers in hexadecimal. Bonus +points for the device replying to unknown commands with a human-readable status message and printing a brief protocol +overview on boot.</li> +<li>If you're doing something even slightly nontrivial or need moderate throughput (>1k commands per second or >20 byte of +data per command) use a COBS-based protocol. A good starting point is a <tt class="docutils literal">[target <span class="pre">MAC][command</span> <span class="pre">ID][command</span> +arguments]</tt> packet format for multidrop busses. For single-drop you may decide to drop the MAC address.</li> +<li>Always include some sort of "status" command that prints life stats such as VCC, temperature, serial framing errors +and uptime. You'll need some sort of ping command anyway and that command might as well do something useful.</li> +<li>If at all possible, keep your protocol context-free across packets/lines. That is, a certain command should always be +self-contained, and no command should change the meaning of the next packet/line/command that is sent. This is really +important to allow for self-synchronization. If you really need to break up something into multiple commands, say you +want to set a large framebuffer in pieces, do it in a <a class="reference external" href="https://en.wikipedia.org/wiki/Idempotence#Computer_science_meaning">idempotent</a> way: Instead of sending something like <tt class="docutils literal">FRAMEBUFFER +<span class="pre">INCOMING:\n[byte</span> <span class="pre">0-16]\n[byte</span> <span class="pre">17-32]\n[...]\nEND</span> OF FRAME</tt> rather send <tt class="docutils literal">FRAMEBUFFER DATA FOR OFFSET 0: [byte +<span class="pre">0-16]\nFRAMEBUFFER</span> DATA FOR OFFSET 17: [byte <span class="pre">17-32]\n[...]\nSWAP</span> BUFFERS\n</tt>.</li> +</ol> +</div> +</div> + </main><footer> + Copyright © 2023 Jan Sebastian Götte + / <a href="/about/">About</a> + / <a href="/imprint/">Imprint</a> +</footer> +<script> + if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser + contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to + prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition. + In support of an open web, this website does not function with this DRM. Please install a browser such + as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports + ad blockers.`; + </script> + </body> +</html> diff --git a/content/blog/telekom-gpon-sfp/images/edgerouter_interface_config.png b/blog/telekom-gpon-sfp/images/edgerouter_interface_config.png Binary files differindex 72d2a9b..72d2a9b 100644 --- a/content/blog/telekom-gpon-sfp/images/edgerouter_interface_config.png +++ b/blog/telekom-gpon-sfp/images/edgerouter_interface_config.png diff --git a/content/blog/telekom-gpon-sfp/images/edgerouter_route_config.png b/blog/telekom-gpon-sfp/images/edgerouter_route_config.png Binary files differindex fe65051..fe65051 100644 --- a/content/blog/telekom-gpon-sfp/images/edgerouter_route_config.png +++ b/blog/telekom-gpon-sfp/images/edgerouter_route_config.png diff --git a/content/blog/telekom-gpon-sfp/images/edgerouter_sfp_config.png b/blog/telekom-gpon-sfp/images/edgerouter_sfp_config.png Binary files differindex 01da1e7..01da1e7 100644 --- a/content/blog/telekom-gpon-sfp/images/edgerouter_sfp_config.png +++ b/blog/telekom-gpon-sfp/images/edgerouter_sfp_config.png diff --git a/content/blog/telekom-gpon-sfp/images/edgerouter_snat_config.png b/blog/telekom-gpon-sfp/images/edgerouter_snat_config.png Binary files differindex 6e033ac..6e033ac 100644 --- a/content/blog/telekom-gpon-sfp/images/edgerouter_snat_config.png +++ b/blog/telekom-gpon-sfp/images/edgerouter_snat_config.png diff --git a/content/blog/telekom-gpon-sfp/images/edgerouter_snat_config2.png b/blog/telekom-gpon-sfp/images/edgerouter_snat_config2.png Binary files differindex fb7ce32..fb7ce32 100644 --- a/content/blog/telekom-gpon-sfp/images/edgerouter_snat_config2.png +++ b/blog/telekom-gpon-sfp/images/edgerouter_snat_config2.png diff --git a/content/blog/telekom-gpon-sfp/images/sfp_onu_ploam_pw_config.png b/blog/telekom-gpon-sfp/images/sfp_onu_ploam_pw_config.png Binary files differindex 66f6f6a..66f6f6a 100644 --- a/content/blog/telekom-gpon-sfp/images/sfp_onu_ploam_pw_config.png +++ b/blog/telekom-gpon-sfp/images/sfp_onu_ploam_pw_config.png diff --git a/content/blog/telekom-gpon-sfp/images/sfp_onu_reset.png b/blog/telekom-gpon-sfp/images/sfp_onu_reset.png Binary files differindex 13c2ca6..13c2ca6 100644 --- a/content/blog/telekom-gpon-sfp/images/sfp_onu_reset.png +++ b/blog/telekom-gpon-sfp/images/sfp_onu_reset.png diff --git a/content/blog/telekom-gpon-sfp/images/sfp_onu_web_if.png b/blog/telekom-gpon-sfp/images/sfp_onu_web_if.png Binary files differindex dea0b8f..dea0b8f 100644 --- a/content/blog/telekom-gpon-sfp/images/sfp_onu_web_if.png +++ b/blog/telekom-gpon-sfp/images/sfp_onu_web_if.png diff --git a/blog/telekom-gpon-sfp/index.html b/blog/telekom-gpon-sfp/index.html new file mode 100644 index 0000000..9be780e --- /dev/null +++ b/blog/telekom-gpon-sfp/index.html @@ -0,0 +1,226 @@ +<!DOCTYPE html> +<html><head> + <meta charset="utf-8"> + <title>Ubiquiti EdgeRouter on Deutsche Telekom GPON Fiber | Home</title> + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="mobile-web-app-capable" content="yes"> + <meta name="color-scheme" content="dark light"> + <link rel="stylesheet" href="/style.css"> +</head> +<body><nav> + <div class="internal"> + + <a href="/" title="Home">Home</a> + <a href="/blog/" title="Blog">Blog</a> + <a href="/projects/" title="Projects">Projects</a> + <a href="/about/" title="About">About</a> + </div> + <div class="external"> + <a href="https://git.jaseg.de/" title="cgit">cgit</a> + <a href="https://github.com/jaseg" title="Github">Github</a> + <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a> + <a href="https://chaos.social/jaseg" title="Mastodon">Mastodon</a> + </span> +</nav> + + <header> + <h1>Ubiquiti EdgeRouter on Deutsche Telekom GPON Fiber</h1> +<ul class="breadcrumbs"> + <li><a href="/">jaseg.de</a></li> + <li><a href="/blog/">Blog</a></li><li><a href="/blog/telekom-gpon-sfp/">Ubiquiti EdgeRouter on Deutsche Telekom GPON Fiber</a></li> +</ul> + <strong>2022-02-21</strong> + </header> + <main> + <div class="document"> + + +<div class="section" id="disclaimer"> +<h2>Disclaimer</h2> +<p>I provide this guide as a reference for other knowledgeable users without any warranty. Please feel free to use this as +a resource but do not hold me responsible if this does not work for you. There is a significant chance that due to an +error on my side or due to Telekom changing their setup this guide will not work for you, and you may end up having to +pay for an unsuccessful Telekom technician visit. That is your own risk, and I do not assume any liability.</p> +</div> +<div class="section" id="tl-dr"> +<h2>Tl;dr</h2> +<p>The "Telekom Digitalisierungsbox Glasfasermodem" is a GPON ONT in SFP form factor that works with an Ubiquiti EdgeRouter +6P's SFP port. You can order it from Telekom or other vendors using the Telekom P/N 40823569 or its EAN 4718937619382. +It costs about the same as the separate plastic box modem, but saves a lot of space and does not require a separate +power supply.</p> +<p>To configure, first access the SFP ONT's web interface at <tt class="docutils literal">10.10.1.1</tt> by configuring your SPF port's IP to static +<tt class="docutils literal">10.10.1.2</tt>. User credentials are either admin/admin or admin/1234. In the web interface, set put PLOAM password into the +"SLID" setting in ASCII mode, then save & reboot the device. Now, configure PPPoE on the router's SFP port using the +PPPoE UID <tt class="docutils literal">[anschlusskennung] [zugangsnummer] "#" [mitbenutzernummer] <span class="pre">"@t-online.de"</span></tt> and your "Persönliches Kennwort" as +PPPoE password. Set the VLAN to <tt class="docutils literal">7</tt>, and you are good to go.</p> +</div> +<div class="section" id="background"> +<h2>Background</h2> +<p>I moved into a new apartment that has a fiber internet connection operated by Deutsche Telekom. Having made some poor +experiences with AVM's FritzBox brand of routers that is commonly used by German carriers, I decided to use my own +Router instead of the one provided by Deutsche Telekom. Like other German providers, Telekom charges exorbitant amounts +in monthly fees for their routers, so even though my choice ended up being a high-end piece of commercial equipment I +will still be cheaper than going with Telekom's much shittier device when added up over a two-year contract period.</p> +<p>The hardware I chose is the Ubiquiti EdgeRouter 6P. This device is from Ubiquiti's commercial lineup and is intended to +power something like a small branch office of a company. It comes in a small form factor (as opposed to larger rackmount +units), it does not consume a lot of power, it has five PoE-capable Ethernet ports which I can directly connect up to +the Ubiquiti Unifi UAP access point that I already have, and it has a powerful configuration interface. It can even +act as a VPN endpoint!</p> +<p>Telekom's fiber internet offering for residential customers is GPON-based. GPON stands for "Gigabit Passive Optical +Network" and means that instead of patching through one fiber or pair of fibers to each customer, several customers in +one building are connected to a single fiber through optical splitters. These optical splitters are passive, i.e. they +are just fancy pieces of glass and fibers and do not require electrical power. The advantage of GPON is lower initial +cost for the operator, the disadvantage is that competing providers can only ever hope to get traffic handed through by +Telekom and will never be able to use their own equipment on the "network" end of the fiber.</p> +<p>Telekom wants you to connect to its fiber network through a small plastic box that they call "modem", and that the rest +of the world calls "ONT", or Optical Network Terminator. Telekom's ONT has an upstream optical port with an LC +connector, and a regular RJ45 ethernet port downstream. The "modem" in fact contains an entire linux system that +terminates the ITU-standard suite of protocols that is used to manage what happens on the fiber, e.g. scheduling of +transmission slots and adjustment of transmitter laser power.</p> +<p>Looking at Telekom's plastic box ONT and my nice and shiny EdgeRouter, I was not a fan of this solution. Doing some +research I found out that you can in fact get GPON ONTs in an SFP module form factor. My EdgeRouter has an SFP slot, so +if I could get one of these that is compatible with Telekom's GPON flavor I could theoretically just plug it into my +EdgeRouter's SFP slot with no separate power supply needed, saving a lot of space in the process.</p> +<p>Finding a GPON SFP ONT that is compatible with Telekom's network turned out to be the hard part. While there are lots of +commercial devices that look like they <em>should be</em> compatible, I could not be sure and I did not feel like sinking lots +of money and weeks of trial and error into figuring out which are and which are not. After about half a dozen calls with +various Telekom customer service departments I found the solution that ultimately ended up working: For their business +customer fiber internet offering, Telekom uses the same GPON standard, but different ONT equipment. Their router for +business customers is called "Digitalisierungsbox" and it in fact comes with an SFP GPON ONT. And, as it turns out, you +can order that SFP GPON ONT separately for about 50 € (the same as the plastic box one) from either Telekom or a number +of independent online stores. The Telekom part number of the thing is 40823569, the EAN is 4718937619382.</p> +<p>Below is a list of steps that I had to undertake in order to get my EdgeRouter/SFP ONT setup to work.</p> +</div> +<div class="section" id="hardware-setup"> +<h2>Hardware Setup</h2> +<p>The hardware setup is really simple. The SFP ONU is plugged into the EdgeRouter's SFP port. The ONU is connected to +the Telekom Fiber through the LC/APC to SC/APC adapter cable that is included in its package. Telekom's technician will +install an LC/APC coupler to join both cables. To configure the EdgeRouter, connect yourself through an ethernet cable +<em>on port 2</em>. Ubiquiti's setup wizards assume the WAN interface is either port 1 or the SFP port (port 5), and default to +use port 2 as their LAN interface even when port 5 is configured as the only WAN port. The default IP for the EdgeRouter +is <tt class="docutils literal">192.168.1.1</tt>, and the default UID/PW is ubnt/ubnt.</p> +</div> +<div class="section" id="configuration"> +<h2>Configuration</h2> +<div class="section" id="getting-access-to-the-sfp-onu-s-config-interface"> +<h3>Getting access to the SFP ONU's config interface</h3> +<p>In this section I am assuming you want to configure the SFP ONU while it is plugged into the EdgeRouter from a laptop +connected to the EdgeRouter's ethernet port 2. To do this, we have to first configure the right IP/subnet on the +EdgeRouter's SFP interface, then patch connections between the SFP ONU and the laptop through the EdgeRouter.</p> +<ol class="arabic simple"> +<li>First, inside the EdgeRouter's config interface we need to configure a static IP with accompanying SNAT rule on the +SFP port to allow us to access the SFP module's web interface through the laptop connected to the EdgeRouter. For +this, configure the eth5 interface (which is the SFP port) to use the static IP <tt class="docutils literal">10.10.1.2/24</tt>.</li> +</ol> +<figure style="width: 20em"> + <a href="images/edgerouter_sfp_config.png"> + <img src="images/edgerouter_sfp_config.png" alt="The EdgeRouter's graphical configuration interface showing IP + address 10.10.1.2/24 being configured for interface eth5, which is the SFP interface."> + </a> + <figcaption>SFP interface configuration to access the SFP ONU from a laptop connected to the EdgeRouter's LAN + port</figcaption> +</figure><ol class="arabic simple" start="2"> +<li>With the SFP port assigned an IP address, we need to add a NAT rule to forward connections from the configuration +laptop on eth2 to the SFP port. We do this by adding a source NAT rule with masquerading enabled, for the TCP +protocol, with destination address <tt class="docutils literal">10.10.1.0/24</tt> (the SFP config interface's private network).</li> +</ol> +<figure style="width: 20em"> + <a href="images/edgerouter_snat_config.png"> + <img src="images/edgerouter_snat_config.png" alt="The EdgeRouter's graphical configuration interface showing a + source NAT being configured for interface eth5 for TCP protocol connections to destination address 10.10.1.1 + using masquerading."> + </a> + <figcaption>Source NAT configuration to access the SFP ONU from LAN. eth5, masquerading on, TCP, destination + 10.10.1.1 (the SFP ONU's IP).</figcaption> +</figure><ol class="arabic simple" start="3"> +<li>Finally, make sure that your laptop will actually use the EdgeRouter as its gateway for IPs within <tt class="docutils literal">10.10.1.0/24</tt>. +On the laptop, disable any VPNs, disconnect your Wifi and make sure that IP r shows a default route pointing at the +EdgeRouter's <tt class="docutils literal">192.168.1.1</tt>. If that isn't the case, on Linux you can manually add the necessary route by using +<tt class="docutils literal">sudo ip r a 10.10.1.0/24 via 192.168.1.1 dev enp5s0</tt></li> +</ol> +<p>After setting up this temporary route, you should be able to access the SFP ONU's configuration web interface by +pointing a browser at <tt class="docutils literal"><span class="pre">http://10.10.1.1/</span></tt> Just make sure you use plain-text HTTP here, not secure HTTP**S**. The +default login credentials for the device are admin/1234.</p> +<figure style="width: 30em"> + <a href="images/sfp_onu_web_if.png"> + <img src="images/sfp_onu_web_if.png" alt="The SFP ONU configuration web interface is a basic-looking website with + a big Zyxel logo on it. It has menu options named status, setup and management. It shows a system overview + page that lists the device's uptime and software version."> + </a> + <figcaption>The SFP ONU's web interface.</figcaption> +</figure></div> +<div class="section" id="configuring-the-ploam-password-slid-ont-installationskennung"> +<h3>Configuring the PLOAM password / SLID / ONT-Installationskennung</h3> +<p>On the SFP ONU's web interface, we only have to change one single setting: Under "Setup", we have to set what the SFP +ONU calls "SLID" to the PLOAM password for the interface. Telekom calls this the "ONT-Installationskennung". You get +this from your Telekom technician. In the config interface, select ASCII mode and enter the number using the format +<tt class="docutils literal">ABCD000000</tt> with four capital letters followed by six zeros. If necessary, you can read the SFP ONU's serial number +on this page.</p> +<figure style="width: 30em"> + <a href="images/sfp_onu_ploam_pw_config.png"> + <img src="images/sfp_onu_ploam_pw_config.png" alt="The SFP ONU configuration web interface shows its SLID + configuration page. A text field labelled SLID asks the user to enter a value of at most ten characters. As + an example, abcdefg123 is listed."> + </a> + <figcaption>The SFP ONU's config interface to set SLID/PLOAM PW/ONT-Installationskennung.</figcaption> +</figure><p>Press "Save Config" on the top right of the web page, then select "Reset ONU" and click "Apply" under the "Reset ONU" +link on the left. Make sure to not select the factory reset option instead.</p> +<figure style="width: 30em"> + <a href="images/sfp_onu_reset.png"> + <img src="images/sfp_onu_reset.png" alt="The SFP ONU configuration web interface shows its reset ONU page. There + are two options labelled Reset ONU and Reset to factory default settings. The reset ONU option is + selected."> + </a> + <figcaption>Rebooting the SFP ONU.</figcaption> +</figure><p>With the ONU configured, after the reset the "GPON Information" page from the left menu under "Status" from the top menu +should show <tt class="docutils literal">GPON Line Status: O5</tt>. You can now remove the SNAT rule and IP address from the SFP interface in the +EdgeRouter's config. I recommend this since there is no way to change the ONU's default credentials, and leaving the +SNAT rule in place makes it vulnerable to attacks from your LAN. If you use the EdgeRouter's setup wizard in the next +step, that wizard will reset all of these settings.</p> +</div> +<div class="section" id="configuring-pppoe-and-nat"> +<h3>Configuring PPPoE and NAT</h3> +<p>Our ONU now has a low-level connection to Telekom's fiber network. The next step is to configure the EdgeRouter to +authenticate with the ONU through PPPoE. The easiest way to do this is to use the EdgeRouter's "Basic Setup" wizard as +described in the <cite>EdgeOS User Guide</cite>. In the wizard, select the SFP port (<tt class="docutils literal">eth5</tt>) as the internet/WAN port. Select +<tt class="docutils literal">Internet Connection Type</tt> as <tt class="docutils literal">PPPoE</tt>, then enter the PPPoE credentials you got from your Telekom technician. The +password is your "Persönliches Kennwort" that you also use to log in to your customer account on Telekom's website. The +account name is <tt class="docutils literal">[anschlusskennung] [zugangsnummer] "#" [mitbenutzernummer] <span class="pre">"@t-online.de"</span></tt>, so something like +<tt class="docutils literal"><span class="pre">002712345678012345678901#0001@t-online.de</span></tt>. Enable "Internet connection is on VLAN" and enter VLAN ID <tt class="docutils literal">7</tt>. This is +necessary because of the way Telekom set up their triple play (TV/phone/internet) service. After following through with +the wizard, your internet should be already working on port 2 of the router. Note that despite selecting the SFP port as +the router's WAN port, the wizard will still reserve port 1 (<tt class="docutils literal">eth0</tt>) for another WAN interface, so you will only be +able to access the configuration interface through port 2 (<tt class="docutils literal">eth1</tt>) after the wizard is done. You can of course change +this later.</p> +<p>That's it, you're done and your internet should be working!</p> +</div> +</div> +<div class="section" id="having-fun-with-the-spf-gpon-onu"> +<h2>Having Fun with the SPF GPON ONU</h2> +<p>If you want to dig deeper into the internals of Telekom's GPON implementation, the SFP ONU's firmware is a great +starting point. Default credentials are all admin/admin or admin/1234 and you can even get a regular busybox shell on +the device through SSH. The device's firmware is based on OpenWRT, and the source for large parts of the core control +components can be found under open source licenses as well. While I would strictly advice you to not mess around with +the actual modem settings because due to GPON you share a medium with your neighbors and might very well disrupt their +internet if you mess up, inspecting the ONU's firmware is a great way to learn about the inner workings of a modern GPON +network.</p> +<p>If you are interested in messing around with the SFP ONU, there is a github repository where interesting thins are +collected <a class="reference external" href="https://github.com/xvzf/zyxel-gpon-sfp/issues">here</a>.</p> +</div> +</div> + </main><footer> + Copyright © 2023 Jan Sebastian Götte + / <a href="/about/">About</a> + / <a href="/imprint/">Imprint</a> +</footer> +<script> + if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser + contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to + prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition. + In support of an open web, this website does not function with this DRM. Please install a browser such + as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports + ad blockers.`; + </script> + </body> +</html> diff --git a/content/blog/thors-hammer/images/thors_hammer_breadboard.jpg b/blog/thors-hammer/images/thors_hammer_breadboard.jpg Binary files differindex 4504d83..4504d83 100644 --- a/content/blog/thors-hammer/images/thors_hammer_breadboard.jpg +++ b/blog/thors-hammer/images/thors_hammer_breadboard.jpg diff --git a/content/blog/thors-hammer/images/thors_hammer_schematic.jpg b/blog/thors-hammer/images/thors_hammer_schematic.jpg Binary files differindex 3061f61..3061f61 100644 --- a/content/blog/thors-hammer/images/thors_hammer_schematic.jpg +++ b/blog/thors-hammer/images/thors_hammer_schematic.jpg diff --git a/blog/thors-hammer/index.html b/blog/thors-hammer/index.html new file mode 100644 index 0000000..0030a1f --- /dev/null +++ b/blog/thors-hammer/index.html @@ -0,0 +1,88 @@ +<!DOCTYPE html> +<html><head> + <meta charset="utf-8"> + <title>Thor's Hammer | Home</title> + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="mobile-web-app-capable" content="yes"> + <meta name="color-scheme" content="dark light"> + <link rel="stylesheet" href="/style.css"> +</head> +<body><nav> + <div class="internal"> + + <a href="/" title="Home">Home</a> + <a href="/blog/" title="Blog">Blog</a> + <a href="/projects/" title="Projects">Projects</a> + <a href="/about/" title="About">About</a> + </div> + <div class="external"> + <a href="https://git.jaseg.de/" title="cgit">cgit</a> + <a href="https://github.com/jaseg" title="Github">Github</a> + <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a> + <a href="https://chaos.social/jaseg" title="Mastodon">Mastodon</a> + </span> +</nav> + + <header> + <h1>Thor's Hammer</h1> +<ul class="breadcrumbs"> + <li><a href="/">jaseg.de</a></li> + <li><a href="/blog/">Blog</a></li><li><a href="/blog/thors-hammer/">Thor's Hammer</a></li> +</ul> + <strong>2018-05-03</strong> + </header> + <main> + <div class="document"> + + +<p>In case you were having an inferiority complex because your friends' IBM Model M keyboards are so much louder than the +shitty rubber dome freebie you got with your pc... Here's the solution: Thor's Hammer, a simple typing cadence enhancer +for <a class="reference external" href="https://en.wikipedia.org/wiki/PS/2_port">PS/2</a> keyboards.</p> +<figure> + <video controls loop> + <source src="video/thors_hammer.mov" type="video/h264"> + <source src="video/thors_hammer.webm" type="video/webm"> + Your browser does not support the HTML5 video tag. + </video> + <figcaption>A demonstration of the completed project. + + <a href="video/thors_hammer.mov">h264 download</a> / + <a href="video/thors_hammer.webm">webm download</a> + </figcaption> +</figure><p>The connects to the keyboard's PS/2 clock line and briefly actuates a large solenoid on each key press. An interesting +fact about PS/2 is that the clock line is only active as long as either the host computer or the input device actually +want to send data. In case of a keyboard that's the case when a key is pressed or when the host changes the keyboard's +LED state, otherwise the clock line is silent. We ignore the LED activity for now as it's generally coupled to key +presses. By just triggering an NE555 configured as astable flipflop we can stretch each train of clock pulses to a +pulse a few tens of milliseconds long that is enough to actuate the solenoid.</p> +<figure> + <img src="images/thors_hammer_schematic.jpg" alt="The schematic of the PS2 driver"> + <figcaption>The schematic of the driver stretching the PS/2 clock pulses to drive the solenoid.</figcaption> +</figure><p>Since PS/2 sends each key press and key release separately this circuit will pulse twice per keystroke. It would be +possible to ignore one of them but I figure the added noise just adds to the experience.</p> +<p>Built on a breadboard, the circuit looks like this.</p> +<figure> + <img src="images/thors_hammer_breadboard.jpg" alt="The circuit built on a breadboard"> + <figcaption>The completed circuit built up on a breadboard and attached to a keyboard.</figcaption> +</figure><p>Since my solenoid did not have a tensioning spring I used a rubber band and some vinyl tape to make an adjustable +tensioner. The small orange USB hub serves as an end-stop because I had nothing else of the right shape. The sound and +resonance of the thing can be adjusted to taste by moving the end stop, adjusting the tensioning rubber and tuning the +excitation duration using the potentiometer. My particular solenoid was a bit slow so I added some pieces of circuit +board as shims between the plunger and the case to limit the plunger's travel inside the solenoid core.</p> +</div> + </main><footer> + Copyright © 2023 Jan Sebastian Götte + / <a href="/about/">About</a> + / <a href="/imprint/">Imprint</a> +</footer> +<script> + if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser + contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to + prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition. + In support of an open web, this website does not function with this DRM. Please install a browser such + as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports + ad blockers.`; + </script> + </body> +</html> diff --git a/content/blog/thors-hammer/video/thors_hammer.mkv b/blog/thors-hammer/video/thors_hammer.mkv Binary files differindex c9581e9..c9581e9 100644 --- a/content/blog/thors-hammer/video/thors_hammer.mkv +++ b/blog/thors-hammer/video/thors_hammer.mkv diff --git a/content/blog/thors-hammer/video/thors_hammer.mov b/blog/thors-hammer/video/thors_hammer.mov Binary files differindex fff65a8..fff65a8 100644 --- a/content/blog/thors-hammer/video/thors_hammer.mov +++ b/blog/thors-hammer/video/thors_hammer.mov diff --git a/content/blog/thors-hammer/video/thors_hammer.webm b/blog/thors-hammer/video/thors_hammer.webm Binary files differindex 2bcf1ca..2bcf1ca 100644 --- a/content/blog/thors-hammer/video/thors_hammer.webm +++ b/blog/thors-hammer/video/thors_hammer.webm diff --git a/content/blog/wifi-led-driver/images/board_in_case.jpg b/blog/wifi-led-driver/images/board_in_case.jpg Binary files differindex 843900a..843900a 100644 --- a/content/blog/wifi-led-driver/images/board_in_case.jpg +++ b/blog/wifi-led-driver/images/board_in_case.jpg diff --git a/content/blog/wifi-led-driver/images/board_in_case.small.jpg b/blog/wifi-led-driver/images/board_in_case.small.jpg Binary files differindex 5c0aa81..5c0aa81 100644 --- a/content/blog/wifi-led-driver/images/board_in_case.small.jpg +++ b/blog/wifi-led-driver/images/board_in_case.small.jpg diff --git a/content/blog/wifi-led-driver/images/boards.jpg b/blog/wifi-led-driver/images/boards.jpg Binary files differindex 79f8154..79f8154 100644 --- a/content/blog/wifi-led-driver/images/boards.jpg +++ b/blog/wifi-led-driver/images/boards.jpg diff --git a/content/blog/wifi-led-driver/images/boards.small.jpg b/blog/wifi-led-driver/images/boards.small.jpg Binary files differindex 6f5c28c..6f5c28c 100644 --- a/content/blog/wifi-led-driver/images/boards.small.jpg +++ b/blog/wifi-led-driver/images/boards.small.jpg diff --git a/content/blog/wifi-led-driver/images/layout.png b/blog/wifi-led-driver/images/layout.png Binary files differindex 11fc50e..11fc50e 100644 --- a/content/blog/wifi-led-driver/images/layout.png +++ b/blog/wifi-led-driver/images/layout.png diff --git a/content/blog/wifi-led-driver/images/schematic.png b/blog/wifi-led-driver/images/schematic.png Binary files differindex 8294f12..8294f12 100644 --- a/content/blog/wifi-led-driver/images/schematic.png +++ b/blog/wifi-led-driver/images/schematic.png diff --git a/blog/wifi-led-driver/index.html b/blog/wifi-led-driver/index.html new file mode 100644 index 0000000..c3ed198 --- /dev/null +++ b/blog/wifi-led-driver/index.html @@ -0,0 +1,146 @@ +<!DOCTYPE html> +<html><head> + <meta charset="utf-8"> + <title>Wifi Led Driver | Home</title> + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="mobile-web-app-capable" content="yes"> + <meta name="color-scheme" content="dark light"> + <link rel="stylesheet" href="/style.css"> +</head> +<body><nav> + <div class="internal"> + + <a href="/" title="Home">Home</a> + <a href="/blog/" title="Blog">Blog</a> + <a href="/projects/" title="Projects">Projects</a> + <a href="/about/" title="About">About</a> + </div> + <div class="external"> + <a href="https://git.jaseg.de/" title="cgit">cgit</a> + <a href="https://github.com/jaseg" title="Github">Github</a> + <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a> + <a href="https://chaos.social/jaseg" title="Mastodon">Mastodon</a> + </span> +</nav> + + <header> + <h1>Wifi Led Driver</h1> +<ul class="breadcrumbs"> + <li><a href="/">jaseg.de</a></li> + <li><a href="/blog/">Blog</a></li><li><a href="/blog/wifi-led-driver/">Wifi Led Driver</a></li> +</ul> + <strong>2018-05-02</strong> + </header> + <main> + <div class="document"> + + +<div class="section" id="project-motivation"> +<h2>Project motivation</h2> +<!-- FIXME finished project picture with LED tape --> +<figure> + <img src="images/board_in_case.small.jpg"> + <figcaption>The completed driver board installed in the 3D-printed case. This device can now be connected to + 12V and two segments of LED tape that can then be controlled trough Wifi. The ESP8266 module goes on the pin + header on the left and was removed for this picture. + </figcaption> +</figure><p>After the <a class="reference external" href="http://jaseg.de/blog/multichannel-led-driver/">multichannel LED driver</a> was completed, I was just getting used to controlling LEDs at 14-bit resolution. +I liked the board we designed in this project, but at 32 channels it was a bit large for most use cases. Sometimes I +just want to pop a piece of LED tape or two somewhere, but I don't need a full 32 channels of control. I ended up +thinking that a smaller version of the 32-channel driver that didn't require a separate control computer would be +handy. So I sat down and designed a variant of the design with only 8 channels instead of 32 and an on-board <a class="reference external" href="https://en.wikipedia.org/wiki/ESP8266">ESP8266</a> +module instead of the <a class="reference external" href="https://en.wikipedia.org/wiki/RS-485">RS485</a> transceiver for WiFi connectivity.</p> +</div> +<div class="section" id="the-electronics"> +<h2>The Electronics</h2> +<p>The schematic was mostly copy-pasted from the 32-channel design. The PCB was designed from scratch. This time, I went +for a 5x7cm form factor to allow for enough room for all connectors and to give the <a class="reference external" href="https://en.wikipedia.org/wiki/ESP8266">ESP8266</a>'s WiFi antenna enough +space. The board has two 5-pin <a class="reference external" href="https://www.phoenixcontact.com/online/portal/de?uri=pxc-oc-itemdetail:pid=1757019&library=dede&tab=1">Phoenix-style</a> for two RGB-White (RGBW) tapes and one 2-pin <a class="reference external" href="https://www.phoenixcontact.com/online/portal/de?uri=pxc-oc-itemdetail:pid=1757019&library=dede&tab=1">Phoenix-style</a> connector for +12V power input. The control circuitry and the serial protocol are unchanged, but the <a class="reference external" href="http://www.st.com/resource/en/datasheet/stm32f030f4.pdf">STM32</a> now talks to an <a class="reference external" href="http://www.watterott.com/de/ESP8266-WiFi-Serial-Transceiver-Modul">ESP-01</a> +module running custom firmware.</p> +<p>The LEDs are driven using a <a class="reference external" href="http://www.ti.com/lit/ds/symlink/sn74hc595.pdf">74HC595</a> shift register controlling a bunch of <a class="reference external" href="http://aosmd.com/pdfs/datasheet/AO3400.pdf">AO3400</a> <a class="reference external" href="https://en.wikipedia.org/wiki/MOSFET">MOSFETs</a>, with resistors in front of +the <a class="reference external" href="https://en.wikipedia.org/wiki/MOSFET">MOSFETs</a>' gates to slow down the transitions a bit to reduce brighntess nonlinearities and <a class="reference external" href="https://en.wikipedia.org/wiki/Electromagnetic_interference">EMI</a> resulting from +ringing of the LED tape's wiring inductance.</p> +<p>The board has two spots for either <a class="reference external" href="https://en.wikipedia.org/wiki/Resettable_fuse">self-resettable fuses (polyfuses)</a> or regular melting-wire <a class="reference external" href="https://en.wikipedia.org/wiki/Fuse_(electrical)">fuses</a> in +a small <a class="reference external" href="https://en.wikipedia.org/wiki/Surface-mount_technology">SMD</a> package, one for each RGBW output. For low currents the self-resettable fuses should be okay but at higher +currents their <a class="reference external" href="http://m.littelfuse.com/~/media/electronics/datasheets/resettable_ptcs/littelfuse_ptc_16r_datasheet.pdf.pdf">trip times get long enough that they become unlikely to trip in time to save anything</a>, so plain old non-resettable fuses would be the way to go there.</p> +<!-- FIXME finished board photos --> +<!-- FIXME board with test tape picture --> +<div class="subfigure"> + <figure> + <img src="images/schematic.png"> + <figcaption> + The schematic of the driver board, with the ESP8266 on the top left, the STM32 microcontroller for LED + modulation below, the shift register in the middle and the LED drivers and outputs on the right. + <a href="resource/schematic_and_pcb.pdf">Download PDF</a> + </figcaption> + </figure> + <figure> + <img src="images/layout.png"> + <figcaption> + The board layout with the top side being visible. The top side contains the footprint for the ESP8266, the + microcontroller, fuses, filter cap, connectors and the shift register. The LEDs are connected on the left, + with one connector per LED tape segment. The power input connector is on the bottom right. The LED driver + MOSFETs are in small SOT-23 packages on the back of the board. Since this board is not intended for + super-high currents, the MOSFETs are adequately cooled just through the board's copper planes. + <a href="resource/schematic_and_pcb.pdf">Download PDF</a> + </figcaption> + </figure> +</div><figure> + <img src="images/boards.small.jpg"> + <figcaption>The completed PCBs of this project (front) and the `multichannel LED driver`_ project the driver + circuitry was derived from (back). + </figcaption> +</figure></div> +<div class="section" id="the-firmware"> +<h2>The Firmware</h2> +<p>The <a class="reference external" href="http://www.st.com/resource/en/datasheet/stm32f030f4.pdf">STM32</a> firmware only had to be slightly modified to accomodate the reduced channel count since the protocol remains +unchanged. The ESP firmware is based on <a class="reference external" href="https://github.com/Spritetm/esphttpd">esphttpd</a> by <a class="reference external" href="http://spritesmods.com/">Spritetm</a>. The modifications to the webserver firmware are pretty +basic. First, the UART console has been disabled since I use the UART to talk to the STM32. The few bootloader messages +popping out the UART on boot are not an issue, since they're unlikely to contain the fixed 32-bit address prefix the +serial protocol requires for the <a class="reference external" href="http://www.st.com/resource/en/datasheet/stm32f030f4.pdf">STM32</a> to do anything.</p> +<p>Second, I added LED control by adding drivers for the serial protocol and a bunch of colorspace conversion functions. +When I first tested the prototype software, I noticed that color reproduction was extremely poor. When I just sent a +<a class="reference external" href="https://en.wikipedia.org/wiki/HSL_and_HSV">HSV</a> rainbow fade from a python command line, the result looked totally wrong. The fade did not seem to go at a constant +speed and some colors, in particular yellow, orange and greens, were not visible at all. The problem turned out to be a +stark mismatch of the red, green and blue channels of the LED tape and less-than-optimal color reproduction of the pure +colors. I decided to properly measure the LED tape's color reproduction so I could compensate for it in software. This +turned out to be an extremely interesting project, the details of which you can read in my <a class="reference external" href="http://jaseg.de/blog/led-characterization/">LED characterization</a> +article.</p> +<p>Third, I updated the built-in websites with some ad-hoc documentation on how to use the thing and a basic interface for +LED control.</p> +<!-- FIXME screenshot of firmware website --> +</div> +<div class="section" id="making-an-enclosure"> +<h2>Making an enclosure</h2> +<p>To be actually useful, the driver needed a robust enclosure. Bare PCBs are nice for prototyping, but for actually +putting the thing anywhere it needs a case to protect it against random destruction.</p> +<p>The board has four mounting holes with comfortable spacing in its corners to allow easy mounting inside a 3D-printed +case. The case itself is described in an <a class="reference external" href="http://www.openscad.org/">OpenSCAD</a> script. To make it look a little nicer, a little 3D relief is laid +into the lid. The 3D relief is generated with a bit of blender magic. The source <a class="reference external" href="https://en.wikipedia.org/wiki/STL_(file_format)">STL</a> model is loaded into blender, then +blender's amazingly flexible rendering system is used to export a depth map of a projection of the model as a <a class="reference external" href="https://en.wikipedia.org/wiki/Portable_Network_Graphics">PNG</a> file. +This depth map is then imported as a triangle mesh into <a class="reference external" href="http://www.openscad.org/">OpenSCAD</a>.</p> +<p>For the relief to look good, I chose a rather high resolution for the depth map. This unfortunately leads to extreme +memory use and processing time on the part of <a class="reference external" href="http://www.openscad.org/">OpenSCAD</a>, but since I have access to a sufficiently fast machine that is +not a problem. Just be careful if you try opening the <a class="reference external" href="http://www.openscad.org/">OpenSCAD</a> file on your machine, <a class="reference external" href="http://www.openscad.org/">OpenSCAD</a> will probably crash +unless you're on a beefy machine or interrupt it when it starts auto-rendering the file.</p> +<p>The board is mounted into the enclosure using knurled insert nuts that are pressed into a 3D-printed hole using a bit of +violence.</p> +</div> +</div> + </main><footer> + Copyright © 2023 Jan Sebastian Götte + / <a href="/about/">About</a> + / <a href="/imprint/">Imprint</a> +</footer> +<script> + if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser + contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to + prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition. + In support of an open web, this website does not function with this DRM. Please install a browser such + as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports + ad blockers.`; + </script> + </body> +</html> diff --git a/content/blog/wifi-led-driver/resource/lyza_schematic_and_pcb.pdf b/blog/wifi-led-driver/resource/lyza_schematic_and_pcb.pdf Binary files differindex 6532888..6532888 100644 --- a/content/blog/wifi-led-driver/resource/lyza_schematic_and_pcb.pdf +++ b/blog/wifi-led-driver/resource/lyza_schematic_and_pcb.pdf diff --git a/categories/index.html b/categories/index.html new file mode 100644 index 0000000..954dd9d --- /dev/null +++ b/categories/index.html @@ -0,0 +1,49 @@ +<!DOCTYPE html> +<html><head> + <meta charset="utf-8"> + <title>Categories | Home</title> + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="mobile-web-app-capable" content="yes"> + <meta name="color-scheme" content="dark light"> + <link rel="stylesheet" href="/style.css"> +</head> +<body><nav> + <div class="internal"> + + <a href="/" title="Home">Home</a> + <a href="/blog/" title="Blog">Blog</a> + <a href="/projects/" title="Projects">Projects</a> + <a href="/about/" title="About">About</a> + </div> + <div class="external"> + <a href="https://git.jaseg.de/" title="cgit">cgit</a> + <a href="https://github.com/jaseg" title="Github">Github</a> + <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a> + <a href="https://chaos.social/jaseg" title="Mastodon">Mastodon</a> + </span> +</nav> + + <header> + <h1>Categories</h1> +<ul class="breadcrumbs"> + <li><a href="/">jaseg.de</a></li><li><a href="/categories/">Categories</a></li> +</ul> + + </header> + <main class="cards"> + </main><footer> + Copyright © 2023 Jan Sebastian Götte + / <a href="/about/">About</a> + / <a href="/imprint/">Imprint</a> +</footer> +<script> + if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser + contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to + prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition. + In support of an open web, this website does not function with this DRM. Please install a browser such + as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports + ad blockers.`; + </script> + </body> +</html> diff --git a/categories/index.xml b/categories/index.xml new file mode 100644 index 0000000..7e9392f --- /dev/null +++ b/categories/index.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> + <channel> + <title>Categories on Home</title> + <link>http://jaseg.de/categories/</link> + <description>Recent content in Categories on Home</description> + <generator>Hugo -- gohugo.io</generator> + <language>en-us</language> + <copyright>Jan Sebastian Götte</copyright><atom:link href="http://jaseg.de/categories/index.xml" rel="self" type="application/rss+xml" /> + </channel> +</rss> diff --git a/config.toml b/config.toml deleted file mode 100644 index cf271c6..0000000 --- a/config.toml +++ /dev/null @@ -1,74 +0,0 @@ -baseURL = "http://jaseg.de/" -languageCode = "en-us" -title = "Home" -copyright = "Jan Sebastian Götte" -theme = "conspiracy" - -[taxonomies] -category = "Categories" -blog = "Posts" - -[[menu.main]] -name = "Home" -url = "/" -weight = 1 - -[[menu.main]] -name = "Blog" -url = "/blog/" -weight = 2 - -[[menu.main]] -name = "Projects" -url = "/projects/" -weight = 3 - -[[menu.main]] -name = "About" -url = "/about/" -weight = 4 - -[[params.profile_links]] -name = "cgit" -url = "https://git.jaseg.de/" -weight = 1 - -[[params.profile_links]] -name = "Github" -url = "https://github.com/jaseg" -weight = 2 - -[[params.profile_links]] -name = "Gitlab" -url = "https://gitlab.com/neinseg" -weight = 3 - -[[params.profile_links]] -name = "Mastodon" -url = "https://chaos.social/jaseg" -weight = 4 - -[[params.footer_links]] -name = "About" -url = "/about/" -weight = 1 - -[[params.footer_links]] -name = "Imprint" -url = "/imprint/" -weight = 2 - -[[params.homepage_categories]] -title = "Recently updated projects" -key = "projects" -weight = 1 -count = 2 - -[[params.homepage_categories]] -title = "Blog" -key = "blog" -weight = 2 -count = 10 - -[security.exec] -allow = ["^dart-sass-embedded$", "^go$", "^npx$", "^postcss$", "^rst2html$"] diff --git a/content/_index.rst b/content/_index.rst deleted file mode 100644 index ad39a14..0000000 --- a/content/_index.rst +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: jaseg.de ---- - -Hi there, and welcome to my personal website. - -I'm jaseg, and I write about my projects here. You can find long-form articles in the blog, and links to my open-source -projects on the projects page. On the top right of this page, there are links to my git repositories and social media -pages. If you want to learn more about me, head over to the about page. diff --git a/content/about/index.rst b/content/about/index.rst deleted file mode 100644 index 16ae3a6..0000000 --- a/content/about/index.rst +++ /dev/null @@ -1,63 +0,0 @@ ---- -title: "About jaseg" ---- - -About ------ - -Hej, I'm Jan, or jaseg. At the moment I'm doing a PhD (Dr.-Ing.) at TU Darmstadt in Computer Science, specializing on -Hardware Security. This is my personal website where I publish things that I find interesting. - -I self-host my code at `git.jaseg.de <https://git.jaseg.de/>`__, but I am also on `github <https://github.com/jaseg>`__ -and on `gitlab <https://gitlab.com/neinseg>`__. I use github for issue tracking for some of my projects such as -`gerbolyze <https://github.com/jaseg/gerbolyze>`__ and `python-mpv <https://github.com/jaseg/python-mpv>`__. I maintain -the `python-mpv <https://pypi.org/project/python-mpv/>`__ and `gerbolyze <https://pypi.org/project/gerbolyze/>`__ python -packages on PyPI. Release tags on these two repositories are signed with the release signing key found `on github -<https://github.com/jaseg.gpg>`__ and below. - -I am not on any social network, but feel free to write me an email at `hello@jaseg.de -<mailto:hello@jaseg.de?subject=About\ page\ on\ blog.jaseg.de>`__. - -I do not use application-level email encryption such as S/MIME or PGP. If you need a higher level of secrecy than -regular old email provides, please ask around for my signal contact or email me a file encrypted using `age -<https://github.com/FiloSottile/age>`__ with one of the SSH keys listed `on my github -<https://github.com/jaseg.keys>`__. You can find both PGP and other SSH keys that I have used in the past on the -internet, but please consider these keys revoked, and do not use them to encrypt anything you send me. - -Python package release signing key -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -I use this GPG key (key ID ``ED7A208EEEC76F2D``) to sign git release tags of both `gerbolyze <https://github.com/jaseg/gerbolyze>`__ and `python-mpv -<https://github.com/jaseg/python-mpv>`__:: - - -----BEGIN PGP PUBLIC KEY BLOCK----- - mDMEXom49xYJKwYBBAHaRw8BAQdA/KrWMt2MKGIZUvlQZnWjNd6i8/ZYjRsBQqEf - PJ8pJ+20NHB5dGhvbi1tcHYgUmVsZWFzZSBTaWduaW5nIEtleSA8cHl0aG9uLW1w - dkBqYXNlZy5kZT6IlgQTFggAPhYhBONvdTB/Cg7C0UX/XO16II7ux28tBQJeibj3 - AhsDBQkSzAMABQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEO16II7ux28thRYA - /3Yl1RdeUGor6K0RTxce9TIBB+DpLNupJgB9f6onuocpAQC614zQ/RQ6rkGTHCwA - ElFClWRQ5eppj0jpAuH15udqAbg4BF6JuPcSCisGAQQBl1UBBQEBB0A0mrXSv6rj - ajCmZR4H4OtowAx477YS+yWARqo1NtdgJwMBCAeIfgQYFggAJhYhBONvdTB/Cg7C - 0UX/XO16II7ux28tBQJeibj3AhsMBQkSzAMAAAoJEO16II7ux28tMZwBAIUpHHvP - gRW2jQuzdw1r06kItfFk/0t+mgNUQ2+vtbhzAP98BoWx7lv+bvlIbBaVgLldusj0 - pHnZI/0y3ksMBkdbBw== - =Mr6G - -----END PGP PUBLIC KEY BLOCK----- - -About this site ---------------- - -This site is made with the hugo static site generator. I made the theme myself, feel free to grab a copy at -`git.jaseg.de <https://git.jaseg.de/blog.git/tree/themes/conspiracy?h=main>`__. The nifty auto-reflowing code embeds are -made with some CSS magic I made that you can find in `style.css -<https://git.jaseg.de/blog.git/tree/themes/conspiracy/assets/css/style.css?h=main&id=2fd22e30ce176d8d8a641fd371ad1623b082eaaf#n367>`__. -The body text is typeset in Roboto Slab, created by `Christian Robertson <https://christianrobertson.com/>`__ while -working at Google. The headlines are set in Nyght Serif, a font by `Maksym Kobuzan <https://linktr.ee/mkobuzan>`__. -Check out their other fonts, their work is beautiful! Source code is typeset in Fira Code, a derivate by ... from -Mozilla's `Fira Mono <https://github.com/mozilla/Fira>`__ font, designed by `Erik Spiekermann -<https://spiekermann.com/>`__, `Ralph du Carrois <https://carrois.com/>`__, `Anja Meiners -<https://anjameiners.com/de/hallo/>`__ and Botio Nikoltchev of Carrois Type Design, now succeeded by `bBoxType -<https://bboxtype.com/typefaces/FiraMono/#!layout=specimen>`__ , and Patryk Adamczyk of Mozilla. The photo of mountains -that's used in the background of this site is by `Fabrizio Conti <https://www.conti.photos/>`__ and can be found on -`Unsplash <https://unsplash.com/photos/TUmjK7ZJgbI>`__. - diff --git a/content/blog/_index.rst b/content/blog/_index.rst deleted file mode 100644 index 9bff67d..0000000 --- a/content/blog/_index.rst +++ /dev/null @@ -1,3 +0,0 @@ ---- -title: Blog ---- diff --git a/content/blog/hsm-basics/index.rst b/content/blog/hsm-basics/index.rst deleted file mode 100644 index 306edcd..0000000 --- a/content/blog/hsm-basics/index.rst +++ /dev/null @@ -1,214 +0,0 @@ ---- -title: "Hardware Security Module Basics" -date: 2019-05-17T15:29:20+08:00 ---- - -Hardware Security Modules and Security Research and Cryptography -================================================================ - -On May 17 2019 I gave a short presentation on the fundamentals of hardware security modules at the weekly seminar of -Prof. Mori's security research working group at Waseda University. The motivation for this was that outside of low-level -hardware security people and people working in the financial industry HSMs are not thought about that often. In -particular most network or systems security people would not consider them an option. Also it could turn out to be -really interesting to think about what could be done with an HSM in conjunction with modern cryptography (instead of -just plain old RSA-OAEP and AES-CBC). - -`Click here to download a PDF with the slides for this talk. <mori_semi_hsm_talk_web.pdf>`__ - -Ideas for research in HSMs -========================== - -Preparing for this talk brought me back to some research ideas I've been working on for a while now. Since I'm not sure -I'll find the time to properly research this topic, I thought it would be great to write down some rought outlines first -for future reference. - -The Problem with current HSM tech ---------------------------------- - -Currently, HSMs are only used in certain specific niche applications such as certificate authority key management and -financial transaction data handling. One key reason for this is that HSMs currently don't provide the affordances that -would be needed for them to be adopted more widely by the cryptographic and security engineering community. As far as I -can tell, the two core missing affordances are: - -1. To be more widely adopted, HSMs must become less expensive. Currently, they go for several tens of thousands of Euro, - which puts them outside most budgets. -2. To be more widely adopted, HSMs must provide the standardized programming interfaces familiar to cryptographic - developers. Currently, every HSM vendor has their own custom cryptographic API and a developer will have to train on - one specific vendor's tooling. Furthermore, any documentation of these internals is kept secret behind NDAs. This - constitutes a high barrier to entry, decreasing adoption in particular with young developers accustomed to - open-source ecosystems. - -Attacking cost of implementation --------------------------------- - -The first issue can be addressed by simply creating a viable low-cost alternative. There is no fundamental technical -reason for the high cost of HSMs. This cost is instead due to manufacturers trying to recoup their expenses for R&D as -well as certification from the small volumes HSMs are sold in. - -Compared to system integration and certification the pure R&D cost of HSM defense mechanisms themselves is not too high -in an academic context it should be feasible to develop a sort of HSM blueprint that can then be cheaply produced by -anyone in need. Since the application areas outlined here are far from the core business areas of the clients of -established HSM vendors this would most likely not be a realistic threat to any established vendor's business and a -co-existence of both should not pose any problems in the short term. - -Benefits of an academic HSM standard ------------------------------------- - -Tackling the high cost of current HSM hardware with an open-source HSM blueprint would yield -several academic advantages beyond cost reduction. - -1. An open-source blueprint could serve as an academic reference design to evaluate and compare other HSM designs - against. For instance this would not only allow quantifying the effectiveness of academic security measures but also - allow an evaluation of commercial HSMs. -2. An open-source blueprint could stimulate academic research in this academically very quiet albeit commercially - important area. This research would ultimately benefit everyone employing HSMs by raising security standards in the - field. Since HSMs are never solely relied upon for overal system security both defensive and offensive security - research would yield these benefits. -3. An open-source blueprint would encourage new people to get into the field and both apply HSMs to practical problems - as well as improve HSMs themselves. Currently, this is highly discouraged due to the strictly proprietary nature of - all available systems. -4. Finally, developing an open-source HSM blueprint might yield new findings in adjacent academic areas due to the - hightly multi-disciplinary nature of security research in general and HSM design in particular. - -Scope of an academic HSM standard ---------------------------------- - -An academic HSM blueprint would need to be flexible so that researchers can adapt it to their particular problem. A -modular architecture would lend itself to this flexibility. Fundamentally, there would be three components to this -architecture. First, a **base** containing infrastructure such as the surveillance microcontroller, power supplies, -power supply filtering and hardware DPA countermeasures, and possibly a standardized mechanical and electrical -interface. - -Next to the base, a system integrator would put their *payload*. The nature of this payload is intentionally kept -unspecified, and it might be anything from a cryptographic microcontroller to a small embedded system such as a -raspberry pi single board computer. Keeping the *payload* open like this achieves two benefits: It gives the HSM -blueprint's user *their* familiar tooling and the hardware *they* need, allowing fast adoption. Someone well-versed in -e.g. Javascript could literally implement their cryptography in Javascript, run it on an off-the-shelf raspberry pi and -just apply the HSM blueprint around it. In addition, keeping the *payload* open reduces the scope of what needs to be -implemented. Building a general SDK on top of something like a bare ARM SoC such as a TI OMAP or a Freescale/NXP IMX -would be a considerable additional burden, on top of the actual HSM design. Keeping the *payload* open allows research -to concentrate on the actual point, the HSM design. - -The final and most important component would be a set of *security measures* that can be combined with the base to -form the final HSM. Each of these *security measures* would entail a detailed specification of its design, manufacture -and security properties. These *security measures* could be simple things like tamper switches or potting, but could -also be complex things like security meshes. - -Given these three components -- *base*, *payload* and *security measures* as detailed specifications any engineer should -be able to design and manufacture a HSM customized to their needs. Unifying these three components within the HSM -blueprint would be a set of reference designs. Each reference design would implement a particular parametrization of the -three architectural components with a physical hole cut out where the payload would go.. These reference designs would -for one serve to guide any implementer on the customization and integration of their own derivation from the blueprint. -In addition it would serve as an extremely simple, low-cost point of entry into the ecosystem. A curious researcher -could simply replicate the reference design and put their existing payload inside. Practically this might mean e.g. a -researcher having PCBs produced according to the design files for a reference design for a mesh-based HSM, producing -their own mesh, physically glueing a raspberry pi SBC into the middle of it, and potting the resulting system. Given the -ease of prototype PCB fabrication today this would realistically allow evaluation of HSM technologies on a budget that -is orders of magnitude less than the cost of current HSMs. - -Research ideas for tamper detection mechanisms -============================================== - -The core component of an HSM blueprint would be a suite of tamper detection mechanisms. Following are a few ideas on how -to improve on the current state of the art of membrane tamper switches plus temperature sensors plus PCB and printed -security meshes plus potting. - -DIY or small lab mesh production --------------------------------- -**Analog sensing** meshes are a proven technology where instead of just monitoring for continuity and shorts, analog -parameters of the mesh traces such as inductance and mutual capacitance are monitored. In 2019, `Immler et al. published -a paper <https://tches.iacr.org/index.php/TCHES/article/view/7334>`__ where took this principle and turned it all the -way up. They directly derived a cryptographic secret from the analog properties of their HSM's security mesh in an -attempt to built a `Physically Unclonable Function, or PUF -<https://en.wikipedia.org/wiki/Physical_unclonable_function>`__. The idea with PUFs is that they reproduce some entropy -that comes from random tolerances of their production process. The same PUF will always yield (approximately) the same -key, but since you cannot control these random production variations, in practice the resulting PUF cannot be cloned. -Note however, that its secrets can of course be copied if you find a way to read them out. - -As Immler et al. demonstrated in their paper, you don't need any secret sauce to create an analog mesh sensing circuit. -All you need are a bunch of (admittedly, expensive) off-the-shelf analog ICs. The interesting bit here is that by -applying more advanced analog sensing, weaknesses of an otherwise coarse mesh desing could maybe be alleviated. That is, -instead of monitoring a very fine mesh for continuity, you could instead closely monitor inductance and capacitance of a -more coarse mesh. This trade-off between sensing circuit complexity (resp. cost) and mesh production capabilities may -allow someone with a poorly equipped lab to still make a decent HSM. The question is, how do you produce a "decent" mesh -given only basic tools? Here are some ideas. - -**3D metal patterning techniques** refers to any technique for producing thin, patterned metal structures on a -three-dimensional plastic substrate. The basic process would consist of 3D-printing the polymer substrate, depositing a -thin metal layer on top and then patterning this metal layer. A good starting point here would be the recent work of -`Ben Kraznow`_ on this exact thing. - -**Copper filament methods** would be any method embedding copper wire from a spool in some resin or other matrix. This -could mean either of a systematic approach of carefully winding or folding the copper wire into patterns or a -non-systematic approach of simply stuffing a large tangle of copper wire into a small space. The main challenge with the -former would be to find a non-tedious way of production. The main challenge with the latter would be to find process -parameters that guarantee complete coverage of the HSM without holes or other areas of lower sensitivity to intrusions. -Both approaches would require careful consideration of the overall design including the polymer resin supporting -structure to ensure sensitivity against attacks since copper wire is mechanically much stronger than the micrometre-thin -metal coatings used in patterning techniques. - -Envelope measurement --------------------- - -Finally, I think there is another set of currently under-utilized tamper-detection methods that would be very -interesting to explore. I am not aware of an academic term for these, so I am just going to dub them *envelope -measurement* here. - -The fundamental apporach of a mesh is to build a physical security envelope (the mesh) that physically detects when it -is disturbed (open or short circuits). This approach works well but has the disadvantage that these meshes are rather -complex to manufacture since effectively every part of them is acting as a sensing element. A conceptually more complex -but in practice potentially simpler approach might be to split the functions of security envelope and sensing element. -This would mean that in place of the mesh, some form of passive element such as metal foil forms the security envelope -which is then checked for tampering using a very sensitive sensor inside. This remote-sensing approach might simplify -the manufacture of the envelope itself and thus yield a design that is more easily customized. Following are a few ideas -on how to approach this envelope measurement problem. - -**Ultrasonic** If the HSM is potted, a few ultrasonic transducers could be added inside the potting. With several -transducers, any one could be used to transmit ultrasound while the others measure complex phase and energy of the -signal they receive. The circuitry for this could be made fairly simple if using a static transmit frequency or a low -chirp rate by using a homodyne receiver built around a comparator fed into some timers. This approach would likely -detect any mechanical attack and would also rule out chemical attacks involving liquids (though starting from which -amount of liquid depends on receiver sensitivity). The main disadvantages might be high power consumption and cost and -size of the ultrasonic transducers. Traditional cheap transducers made for air as a transmission medium are fairly large -and might not adequately couple into potting compound. If somehow one could convince a standard small piezo element to -do the same job that would be great as far as cost and size are concerned. A concern in some fringe use cases might be -suceptibility to ambient noise, though this could easily be reduced at the expense of space and heat dissipation -capacity by adding sound dampening on the outside. A likely attack vector against this approach might be using a laser -cutter to drill a hole through the potting compound, then inserting probes carefully chosen to not couple too much -to the potting compound ultrasonically. - -**Light** In either an unpotted HSM or one potted with a transparent (at some wavelengths) potting compound one could -embed LEDs and photodiodes in a similar setup to the ultrasonic setup described above. In contrast to the ultrasound, -the LEDs would literally have to light up the HSM's interior and shadows might be an issue since the HSM is likely some -flat rectangular shape. A possible solution to this would be to coat both the embedded payload and the lid with some -highly reflective paint such as some glossy silver paint or simple white paint. The basic approach might be as simple as -simply turning on several LEDs distributed throughout the HSM in turn and measuring amplitude at several photodetectors, -or as complex as doing a LIDAR-like phase measurement sweeping through a range of frequencies to determine not only -absorption but also phase/distance characteristics between emitter LED and detector photodiode. Using some high-gain TIA -along with a homodyne detector (lock-in amplifier) and changing emitter intensity, very precise measurements of both -absorption and phase might be possible, as might be measurements through almost opaque, diffuse potting compounds such -as a grey epoxide resin. The main disadvantages of this method would likely be the need to thoroughly light-proof the -entire HSM (likely by wrapping it in metal foil) and the potentially high cost of transmitter and receiver circuitry -(nice TIAs aren't cheap). To be effective against attacks using e.g. very fine drills and probes the system would likely -have to be very sensitive. - -**Radar** Finally, one could turn to standard radar techniques to fingerprint the inside of the HSM. The goal here would -be fingerprinting instead of mapping since only changes need to be detected. In this approach one could use homodyne -detection to improve sensitivity and reduce receiver complexity, and sweep frequencies similar to an FMCW radar (but -probably without exploiting the self-demodulation effect). Besides high cost, this approach has two disadvantages. -First, such a system would likely not go beyond 24GHz or maybe 40GHz due to component availability issues. Even at 40GHz -the wavelength in the potting compound would be in the order of magnitude of several millimeters. Fine intrusions using -some tool chosen to not interact too much with the EM field inside the HSM such as a heated ceramic needle or simply a -laser cutter might not be detectable using this approach. In any case, this system would certainly not be able to detect -small holes piercing the HSM enclosure. The HSM enclosure would have to be made into an RF shield, likely by using some -metal foil in it. - -Overall in the author's opinion these three techniques are most promising in order *Light*, *Ultrasonic*, *Radar*. Light -would prbably provide the best sensitivity at expense of some cost. Ultrasonic might be used in conjunction with light -to cover some additional angles since it is potentially very low-cost. Radar seems hard to engineer into a solution that -works reliably and also would likely be at least an order of magnitude more expensive than the other two technologies -while not providing better sensitivity. - -.. _`Ben Kraznow`: https://www.youtube.com/watch?v=Z228xymQYho -.. _affordances: https://en.wikipedia.org/wiki/Affordance - diff --git a/content/blog/ihsm-worlds-first-diy-hsm/index.rst b/content/blog/ihsm-worlds-first-diy-hsm/index.rst deleted file mode 100644 index 0932fc9..0000000 --- a/content/blog/ihsm-worlds-first-diy-hsm/index.rst +++ /dev/null @@ -1,41 +0,0 @@ ---- -title: "New Paper on Inertial Hardware Security Modules" -date: 2021-11-23T23:42:20+01:00 ---- - -World's First DIY HSM -===================== - -Last week, Prof. Dr. Björn Scheuermann and I have `published our first joint paper on Hardware Security Modules -<https://tches.iacr.org/index.php/TCHES/article/view/9290>`__. In our paper, we introduce Inertial Hardware Security -Modules (IHSMs), a new way of building high-security HSMs from basic components. I think the technology we demonstrate -in our paper might allow some neat applications where some civil organization deploys a service that no one, not even -they themselves, can snoop on. Anyone can built an IHSM without needing any fancy equipment, which makes me optimistic -that maybe the ideas of the `Cypherpunk movement <https://www.activism.net/cypherpunk/manifesto.html>`__ aren't obsolete -after all, despite even the word "crypto" having been co-opted by radical capitalist environmental destructionists. - -An IHSM is basically an ultra-secure enclosure for something like a server or a raspberry pi that even someone with -unlimited resources would have a really hard time cracking without destroying all data stored in it. The principle of an -IHSM is the same as that of a `normal HSM`_. You have a payload that contains really secret data. There's really no way -to prevent an attacker with physical access to the thing from opening it given enough time and abrasive discs for their -angle grinder. So what you do instead is that you make it self-destruct its secrets within microseconds of anyone -tampering with it. Usually, such HSMs are used for storing credit card pins and other financial data. They're expensive -as fuck, all the while being about the same processing speed as a smartphone. Traditional HSMs use printed or -lithographically patterned conductive foils for their security mesh. These foils are not an off-the-shelf component and -are made in a completely custom manufacturing process. To create your own, you would have to re-engineer that entire -process and probably spend some serious money on production machines. - -Inertial HSMs take the concept of traditional HSMs, but replace the usual tamper detection mesh with a few security mesh -PCBs. These PCBs are coarser than traditional meshes by orders of magnitude, and would alone not even be close to enough -to keep out even a moderately motivated attacker. IHSMs fix this issue by spinning the entire tamper detection mesh at -very high speed. To tamper with the mesh, an attacker would have to stop it. This, in turn, can be easily detected by -the mesh's alarm circuitry using a simple accelerometer as a rotation sensor. - -In our paper, we have shown a working prototype of the core concepts one needs to build such an IHSM. To build an IHSM -you only need a basic electronics lab. I built the prototype in our paper at home during one of Germany's COVID -lockdowns. You can have a look at our code and CAD on `my git <https://git.jaseg.de/ihsm.git>`__. What is missing right -now is an integration of all of these fragments into something cohesive that an interested person with the right tools -could go out and build. We are planning to release this sort of documentation at some point, but right now we are -focusing our effort on the next iteration of the design instead. Stay tuned for updates ;) - -.. _`normal HSM`: {{<ref "blog/hsm-basics/index.rst">}} diff --git a/content/blog/kicad-mesh-plugin/index.rst b/content/blog/kicad-mesh-plugin/index.rst deleted file mode 100644 index df15933..0000000 --- a/content/blog/kicad-mesh-plugin/index.rst +++ /dev/null @@ -1,225 +0,0 @@ ---- -title: "Kicad Mesh Plugin" -date: 2020-08-18T13:15:39+02:00 ---- - -.. raw:: html - - <figure> - <img src="images/anim.webp" style="max-width: 20em"> - </figure> - -Tamper Detection Meshes -======================= - -Cryptography is at the foundation of our modern, networked world. From email to card payment infrastructure in brick and -mortar stores, cryptographic keys secure almost every part of our digital lives againts cybercriminals or curious -surveillance capitalists. Without cryptography, many of the things we routinely do in our lives such as paying for -groceries with a credit card, messaging a friend on `Signal <https://signal.org>`_ or unlocking a car with its keyfob -would not be possible. The security of all of these systems in its core lies on the secrecy of cryptographic keys. -Systems differ in what kind of keys they use, how often these keys are replaced and the intricacies of the cryptographic -operations these keys fit into but all have in common that their security relies on keeping the keys secret. - -In practice, this secrecy has been implemented in many different ways. Mass-market software such as browsers or -messenger apps usually relies on some operating system facility to tell the computer "*please keep this piece of memory -away from all other applications*". While on desktop operating systems usually this does not provide much of a barrier -to other programs on the same computer, on modern mobile operating systems this approach is actually quite secure. -However, given sufficient resources no security is perfect. All of these systems can be compromised if the host -operating system is compromised sufficiently, and for organizations with considerable resources a market has sprung up -that offers turn-key solutions for all wiretapping needs. - -In some applications, this level of security has not been considered sufficient. Particularly financial infrastructure -is such a high-profile target that a lot of effort has been put into the security of cryptographic implementations. The -best cryptographic algorithm is useless if it is run on a compromised system (from that system's point of view anyway). -One of the core cryptographic components in financial applications are smartcards like they are used as payment cards in -most countries nowadays. These smartcards contain a small, specialized cryptographic microcontroller that is designed to -be hard to tamper with. Though one of the design goals of the system is to reduce the amount of sensitive information -stored on the card, things such as copying of a card can only be hindered by making the chip hard to read out. - -.. raw:: html - - <figure> - <img src="images/modern_art.svg" style="max-width: 20em"> - </figure> - -With smartcards being the means of choice on one side of the counter in electronic payments, on the other side of the -counter a different technology prevails. Attacks on payment terminals are bound to have much more dire consequences than -attacks on individual cards since one terminal might see hundreds of cards being read every day. For this reason, the -level of attack countermeasures employed in these terminals is a considerable step up from bare smartcards. While a -smartcard is made physically hard to tamper, it does not have a battery and it can only detect tampering once it is -powered by a reader. This allows for well-equipped attackers to use tools such as Focused Ion Beam (FIB) workstations to -circumvent the smartcard's defences while it is powered down, and then power up the card to carry out the actual attack. - -The answer to this problem in electronic payment infrastructure is called *Hardware Security Module*, or HSM. An HSM is -similar to a smartcard in its function (cryptographic processing using keys that are meant to never leave the protection -of the HSM). The one major between the two is that an HSM has its own battery and is continuously powered from its -manufacture to the day it is scrapped. If the HSM looses power at any point in time, it uses a small amount of energy -stored internally to securely wipe all cryptographic secrets from its memory within a few milliseconds. - -Being powered at all times allows the HSM to actively detect and respond to attacks. The most common way this is done is -by wrapping the juicy secret parts in a foil or a printed circuit board that is patterned with a long and convoluted -maze of wires, called a *mesh*. The HSM is continuously monitoring these wires for changes (such as shorts, breaks or -changes in resistance) and will sound the alarm when any are detected. Practically, this presents a considerable hurdle -to any attacker: They have to find a way to disable or circumvent the mesh while it is being monitored by the HSM. In -practice, often this is no insurmountable challenge but it again increases attack costs. - -DIY Meshes -========== - -Throughout my studies in security research I have always had an interest in HSMs. I have taken apart my fair share of -HSMs and at this point, to understand the technology more, I want to experiment with building my own HSM. In last year's -`HSM basics <{{<ref "blog/hsm-basics/index.rst">}}>`_ post I have lined out some ideas for a next generation design that -deviates from the bread-and-butter apporoach of using a mesh as the primary security feature. Before embarking on -practical experiments with these ideas, I want to first take a stab at replicating the current state of the art as best -I can. State of the art meshes often use exotic substrates such as 3D plastic parts with traces chemically deposited on -their surface or special flexible substrates with conductive ink traces. These technologies will likely be too -cumbersome for me to implement myself only for a few prototypes, and industrial manufacturers will most likely be too -expensive. Thus, I will concentrate on regular PCB technology for now. - -The idea of a mesh on a PCB is pretty simple: You have one or several traces that you try to cover every corner of the -mesh PCB's area with. To be most effective, the traces should be as thin and as close together as possible. To increase -the chances of a manipulation being detected, multiple traces can also be used that can then be monitored for shorts -between them. - -While one can feasibly lay out these traces by hand, this really is an ideal application of a simple auto-router. While -general PCB autorouting is *hard*, auto-routing just a few traces to approximate a space-filling curve is not. Since I -am just starting out, I went with the simplest algorithmic solution I could think of. I first approximate the area -designated to the mesh with a square grid whose cells are a multiple of my trace/space size. The mesh will only be drawn -into grid cells that are fully inside the set boundaries. All cells outside or going across the border are discarded in -this step. - -I decided to implement this auto-router in a KiCAD plugin. Though KiCADs plugin API is not the best, it was just about -usable for this task. - -.. raw:: html - - <figure> - <img src="images/kicad-mesh-outline.png" alt="KiCAD showing an irregular board shape with rounded corners and - indents. In the middle of the board there is a footprint for a 4-pin surface-mount pin header."> - <figcaption>The process starts out with the mesh shape being defined inside KiCAD. The mesh's outline is drawn - onto one of the graphical "Eco" layers. A footprint is placed to serve as a placeholder for the mesh's - connections to the outside world. This footprint is later used as the starting point for the mesh generation - algorithm.</figcaption> - </figure> - -.. raw:: html - - <figure> - <img src="images/grid-vis-plain.svg" alt="A vizualization of the grid fitting process. Over the mesh's irregular - outline a grid is drawn. In this picture, all grid cells that are fully inside the grid are shown. Grid cells - that overlap the mesh border are highlighted. Grid cells outside of the mesh border are not drawn."> - <figcaption>A visualization of the grid fitting process. First, a grid large enough to contain the mesh border - is generated. Then, every cell is checked for overlap with the mesh border area. If the cell is fully inside, it - (yellow), it is considered in the mesh generation later. Cells outside (gray) or on the border (red) are - discarded.</figcaption> - </figure> - -After generating the grid, starting from the place I want to connect to the mesh, I walk the grid's cells one by one to -generate a tree that covers the entire grid's area. To set the mesh's starting place I place a footprint on the board -(dark gray in the picture above) whose designator I then tell my script. The tree generation algorithm looks like a -depth-first search, except all checks are random. Depending on the level of randomness used at each step of the -algorithm it yields more or less organized-looking results. Below are five example runs of the algorithm at differing -levels of randomness with the cells colored according to their distance from the tree root. 0% randomness means that the -algorithm is going to try cells in forward direction first on every step, and only then try out left and right. 100% -means that on every step, the algorithm is choosing a new direction at random. - -.. raw:: html - - <div class="subfigure"> - <figure> - <img src="images/cells-0.svg" alt="a completely organized looking grid with spiral patterns all over."> - <figcaption>0%</figcaption> - </figure> - <figure> - <img src="images/cells-25.svg"> - <figcaption>25%</figcaption> - </figure> - <figure> - <img src="images/cells-50.svg"> - <figcaption>50%</figcaption> - </figure> - <figure> - <img src="images/cells-75.svg"> - <figcaption>75%</figcaption> - </figure> - <figure> - <img src="images/cells-100.svg" alt="a completely random looking grid with cells aggregating into ireggular - areas that look like paint splotches."> - <figcaption>100%</figcaption> - </figure> - </div> - -After I have built this tree like you would do in a depth-first search, I draw my one or several mesh mesh traces into -it. The core observation here is that there is only 16 possible ways a cell can be connected: It has four neighbors, -each of which it can either be connected to or not, which results in 2^4 options. If you consider rotations and -mirroring, this works out to rotations or mirrored versions of only six base tiles: The empty tile, a tile with all four -sides connected, a straight through, a 90 degree bend, and a "T"-junction—see the illustration below. - -.. raw:: html - - <figure> - <img src="images/maze_tiles_plain.svg" style="max-width: 20em"> - <figcaption> - There are six possible tile types in our connectivity graph inside its square tiling. This graphic illustrates - all sixteen rotations of these with how they would look in a two-conductor mesh. - </figcaption> - </figure> - -After tiling the grid according to the key above, we get the result below. - -.. raw:: html - - <figure> - <img src="images/tiles-25-small.svg"> - <figcaption> - An auto-routed mesh with traces colored according to tile types. - </figcaption> - </figure> - -.. raw:: html - - <figure> - <img src="images/traces-25-small.svg"> - <figcaption> - The same mesh, but with traces all black. - </figcaption> - </figure> - -Putting it all together got me the KiCAD plugin you can see in the screenshot below. - -.. raw:: html - - <figure> - <img src="images/kicad-mesh-settings2.png"> - <figcaption> - The plugin settings window open. - </figcaption> - </figure> - -.. raw:: html - - <figure> - <img src="images/kicad-mesh-result-large.png"> - <figcaption> - After runing the plugin, the generated mesh looks like this in pcbnew. - </figcaption> - </figure> - -I am fairly happy with the result, but getting there was a medium pain. Especially KiCAD's plugin API is still very -unfinieshed. It is hard to use, most parts are completely undocumented and if you use anything but its most basic parts -things tend to break. One particular pain point for me was that after generating the mesh, the traces have been added to -the board, but are still invisible for some reason. You have to save the board first, then re-load the file for them to -become visible. Also KiCAD crashes whenever the plugin tries to remove a trace, so currently my workflow involves always -making a copy of the board file first and treating mesh generation as a non-reversible finishing step. - -`Check out the code on my cgit <https://git.jaseg.de/kimesh.git/tree/plugin/mesh_dialog.py>`_. - -.. :: - - .. raw:: html - - <figure> - <img src="images/grid-vis-plain.svg" alt=""> - <figcaption></figcaption> - </figure> - diff --git a/content/blog/led-characterization/index.rst b/content/blog/led-characterization/index.rst deleted file mode 100644 index 6ad4d64..0000000 --- a/content/blog/led-characterization/index.rst +++ /dev/null @@ -1,504 +0,0 @@ ---- -title: "LED Characterization" -date: 2018-05-02T11:18:38+02:00 ---- - -Preface -------- - -Recently, I have been working on a `small driver`_ for ambient lighting using 12V LED strips like you can get -inexpensively from China. I wanted to be able to just throw one of these somewhere, stick down some LED tape, hook it up -to a small transformer and be able to control it through Wifi. When I was writing the firmware, I noticed that when -fading between different colors, the colors look *all wrong*! This observation led me down a rabbit hole of color -perception and LED peculiarities. - -The idea of the LED driver was that it can be used either with up to eight single-color LED tapes or, much more -interesting, with up to two RGB or RGBW (red-green-blue-white) LED tapes. For ambient lighting high color resolution was -really important so you could dim it down a lot without flickering. I ended up using the same driver stage I used in the -`multichannel LED driver`_ project for its great color resolution and low hardware requirements. - -.. raw:: html - - <figure> - <img src="images/rgb_cube.svg" alt="An illustration of the RGB color cube."> - <figcaption>An illustration of the RGB color cube. - <a href="https://commons.wikimedia.org/wiki/File:RGB_color_cube.svg">Picture</a> by - <a href="https://commons.wikimedia.org/wiki/User:Maklaan">Maklaan from Wikimedia Commons</a>, - <a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA 3.0</a> - </figcaption> - </figure> - -To make setting colors over Wifi more intuitive I implemented support for HSV colors. RGB is fine for communication -between computers, but I think HSV is easier to work with when manually inputting colors from the command line. RGB is -close to how most monitors, cameras and the human visual apparatus work on a very low level but doesn't match -higher-level human color perception very well. When we describe a color we tend to think in terms of "hue" or -"brightness", and computing a measure of those from RGB values is not easy. - -Colors and Color Spaces ------------------------ - -`Color spaces`_ are a mathematical abstraction of the concept of color. When we say "RGB", most of the time we actually -mean `sRGB`_, a standardized notion of how to map three numbers labelled "red", "green" and "blue" onto a perceived -color. `HSV`_ is an early attempt to more closely align these numbers with our perception. After HSV, a number of other -*perceptual* color spaces such as `XYZ (CIE 1931)`_ and `CIE Lab/LCh`_ were born, further improving this alignment. In -this mathematical model, mapping a color from one color space into another color space is just a coordinate -transformation. - -.. raw:: html - - <figure> - <img src="images/hsv_cylinder.png" alt="An illustration of the HSV color space as a cylinder."> - <figcaption>An illustration of the HSV color space as a cylinder. - <a href="https://commons.wikimedia.org/wiki/File:HSV_color_solid_cylinder.png">Picture</a> by - <a href="https://commons.wikimedia.org/wiki/User:SharkD">SharkD from Wikimedia Commons</a>, - <a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA 3.0</a> - </figcaption> - </figure> - -CIE 1931 XYZ is much larger than any other color space, which is why it is a good basis to express other color spaces -in. In XYZ there are many coordinates that are outside of what the human eye can perceive. Below is an illustration of -the sRGB space within XYZ. The wireframe cube is (0,0,0) to (1,1,1) in XYZ. The colorful object in the middle is what -of sRGB fits inside XYZ, and the lines extending out from it indicate the space that can be expressed in sRGB but not in -XYZ. The fat white curve is a projection of the *monochromatic spectral locus*, that is the curve of points you get in -XYZ for pure visible wavelengths. - -As you can see, sRGB is *much* smaller than XYZ or even the part within the monochromatic locus that we can perceive. In -particular in the blues and greens we loose *a lot* of colors to sRGB. - -.. raw:: html - - <figure> - <video controls loop> - <source src="video/sRGB.mkv" type="video/h264"> - <source src="video/sRGB.webm" type="video/webm"> - Your browser does not support the HTML5 video tag. - </video> - <figcaption>Illustration of the measured sRGB color space within XYZ. The thick, white line is the spectral - locus. - - <a href="video/sRGB.mkv">mkv/h264 download</a> / - <a href="video/sRGB.webm">webm download</a> - </figcaption> - </figure> - -The wrong colors I got when fading between colors were caused by this coordinate transformation being askew. Thinking -over the problem, there are several sources for imperfections: - -* The LED driver may not be entirely linear. For most modulations such as PWM the brightness will be linear starting - from a certain value, but there is probably an offset caused by imperfect edges of the LED current. This offset can be - compensated with software calibration. I built a calibration setup for driver linearity in the `multichannel LED - driver`_ project. Below are pictures of ringing on the edges of an LED driver's waveform. - -* The red, green and blue channels of the LEDs used on the LED tape are not matched. This skews the RGB color space. - In practice, the blue channel of my RGB tape to me *looks* much brighter than the red channel. - -* The precise colors of the red, green and blue channels of the LEDs are unknown. Though the red channel *looks* red, it - may be of a slightly different hue compared to the reference red used in `sRGB`_ which would also skew the RGB color - space. - -.. raw:: html - - <div class="subfigure"> - <figure> - <img src="images/driver_ringing_strong.jpg" alt="Strong ringing on the LED voltage waveform edge at about - 100% overshoot during about 70% of the cycle time."> - <figcaption>The LED strip being at the end of a couple meters of wire caused extremely bad ringing at high - driving frequencies.</figcaption> - </figure> - <figure> - <img src="images/driver_ringing_weak.jpg" alt="Weak ringing on the LED voltage waveform edge at about 30% - overshoot during about 20% of the cycle time."> - <figcaption>Adding a resistor in front of the MOSFET gate to slow the transition dampened the ringing - somewhat, but ultimately it cannot be eliminated entirely.</figcaption> - </figure> - </div> - -These last two errors are tricky to compensate. What I needed for that was basically a model of the *perceived* colors -of the LED tape's color channels. A way of doing his is to record the spectra of all color channels and then evaluate -their respective XYZ coordinates. If all three channels are measured in one go with the same setup the relative -magnitudes of the channels in XYZ will be accurate. - -To map any color to the LEDs, the color's XYZ coordinates simply have to be mapped onto the linear coordinate system -produced by these three points within XYZ. LEDs are mostly linear in their luminous flux vs. current characteristic so -this model will be adequate. The spectral integrals mapping the channels' measured responses to XYZ need only be -calculated once and their results can be used as scaling factors thereafter. - -Measuring the spectrum ----------------------- - -In order to compensate for the cheap LED tape's non-ideal performance I had to measure the LED's red, green and blue -channels' spectra. The obvious thing would be to go out and buy a `spectrograph`_, or ask someone to borrow theirs. The -former is kind of expensive, and I did not want to wait two weeks for the thing to arrive. The latter I could probably -not do every time I got new LED tape. Thus the only choice was to build my own. - -Luckily, building your own spectrometer is really easy. The first thing you need is something that splits incident light -into its constituent wavelengths. In professional devices this is called the *`monochromator`_*, since it allows extraction -of small color bands from the spectrum. The second thing is some sort of optics that project the incident light onto a -screen behind the monochromator. In professional devices lenses or curved mirrors are used. In a simple homebrew job a -pinhole as you would use in a `camera obscura`_ does a remarkably nice job. - -For the monochromator component several things could be used. A prism would work, but I did not have any. The -alternative is a `diffraction grating`_. Professional gratings are quite specialized pieces of equipment and thus -rather expensive. Luckily, there is a common household item that works almost as well: A regular CD or DVD. The -microscopic grooves that are used to record data in a CD or DVD work the same as the grooves in a professional -diffraction grating. - -Household spectra ------------------ - -From this starting point, a few seconds on my favorite search engine yielded an `article by two researchers from the -National Science Museum in Tokyo`_ providing a nice blueprint for a simple cardboard-and-DVD construction for use in -classrooms. I replicated their device using a DVD and it worked beautifully. Daylight and several types of small LEDs I -had around did show the expected spectra. Small red, yellow, green, and blue LEDs showed narrow spectra, daylight one -continuous broad one, and white LEDs a continuous broad one with a distinct bright spot in the blue part. The -single-color LED spectra are quite narrow since they are determined by the LED's semiconductor's band gap, which is -specific to the semiconductor used and is quite precise. White LEDs are in fact a blue LED chip covered with a so-called -*phosphor*. This phosphor is not elementary phosphorus but an anorganic compound that absorbs the LED chip's blue light -and re-emits a broader spectrum of more yellow-ish wavelengths instead. The final LED spectrum is a superposition of -both spectra, with some of the original blue light leaking through the phosphor mixing with the broadband yellow -spectrum of the phosphor. - -.. raw:: html - - <div class="subfigure"> - <figure> - <img src="images/spectrograph_step1_parts.jpg"> - <figcaption>The ingredients. The cup of coffee and Madoka Magica DVD set are essential to the eventual - function of the appartus.</figcaption> - </figure> - <figure> - <img src="images/spectrograph_step2.jpg"> - <figcaption>Step 1: Cut to size and mark down all holes as described in <a - href="http://www.candac.ca/candacweb/sites/default/files/BuildaSpectroscope.pdf">the manual</a></figcaption> - </figure> - <figure> - <img src="images/spectrograph_step3.jpg"> - <figcaption>Step 2: Cut out all holes</figcaption> - </figure> - <figure> - <img src="images/spectrograph_step4_complete.jpg"> - <figcaption>The finished result with the back side showing. The viewing window is on the bottom of the other - side.</figcaption> - </figure> - </div> - - -Now that I had a spectrograph, I needed a somewhat predictable way of measuring the spectrum it gave me. - -Measuring a spectrum --------------------- - -Pointing a camera at the spectrograph would be the obvious thing to do. This produces pretty images but has one critical -flaw: I wanted to acquire quantitative measurements of brightness across the spectrum. Since I don't have a precise -technical datasheet specifying the spectral response of any of my cameras I can't compare the absolute brightness of -different colors on their pictures. Some other sensor was needed. - -.. raw:: html - - <figure> - <img src="images/daylight_spectrum_dvd.jpg"> - <figcaption>The daylight spectrum as seen using a DVD as a grating. - <a href="https://commons.wikimedia.org/wiki/File:SpectresSolaires-DVD.jpg">Picture</a> by - <a href="https://commons.wikimedia.org/wiki/User:Xofc">Xofc from Wikimedia Commons</a>, - <a href="https://creativecommons.org/licenses/by-sa/4.0/">CC-BY-SA 4.0</a> - </figcaption> - </figure> - - -Measuring light intensity -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Looking around my lab, I found a bag of `SFH2701`_ visible-light photodiodes. Their -datasheet includes their spectral response so I can compensate for that, allowing precise-ish absolute intensity -measurements. Just like LEDs, photodiodes are extremely linear across several orders of magnitude. The datasheet of the -classic `BPW34`_ photodiode shows that this photodiode's light current is exactly proportional to illuminance over at -least three orders of magnitude. The `SFH2701`_ datasheet does not include a similar graph but its performance will be -similar. The `SFH2701`_ photodiodes I had at hand were perfect for the job compared to the vintage `BPW34`_ since their -active sensing area is really small (0.6mm by 0.6mm) compared to the BPW34 (a whopping 3mm by 3mm). If I were to use a -`BPW34`_ I would have to insert some small apterture in front of it so it does not catch too broad a part of the -spectrum at once. The `SFH2701`_ is small enough that if I just point it at the projected spectrum directly I will -already get only a small part of the spectrum inside its 0.6mm active area. - -To convert the photodiode's tiny photocurrent into a measurable voltage I built another copy of the `transimpedance -amplifier`_ circuit I already used in the `multichannel LED driver`_. A `transimpedance amplifier`_ is an -amplifiert that produces a large voltage from a small current. The weird name comes from the fact that it works kind of -like an amplified resistor (which can be generalized as an *impedance* electrically). Apply a current to a resistor and -you get a voltage. A transimpedance amplifiert does the same with the difference that its input always stays at 0V, -making it look like an ideal current sink to the connected current source. - -Transimpedance amplifiers are common in optoelectronics to convert small photocurrents to voltages. In this instance I -built a very simple circuit with a dampened transimpedance amplifier stage followed by a simple RC filter for noise -rejection and a regular non-inverting amplifier using another op-amp from the same chip to further boost the filtered -transimpedance amplifier output. I put all the passives setting amplifier response (the gain-setting resistors and the -filter resistor and capacitors) on a small removable adapter so I could easily change them if necessary. I put a small -trimpot on the virtual ground both amplifers use as a reference so I could trim that if necessary. - -.. raw:: html - - <figure> - <img src="images/preamp_schematic.jpg" alt="A drawing of the photodiode preamplifier's schematic"> - <figcaption>The photodiode preamplifier schematic. Schematic drawn with an unlicensed copy of - DaveCAD.</figcaption> - </figure> - -Following are pictures of the preamplifier board. The connectors on the top-left side are two copies of the analog -signal for the ADC and a small panel meter. The SMA connector is used as the photodiode input since coax cables are -generally low-leakage and have built-in shielding. The circuit is powered via the micro-USB connector and the analog -ground bias voltage can be adjusted using the trimpot. - -For easy replacement, all passives setting gain and frequency response are on a small, pluggable carrier PCB made from a -SMD-to-DIP adapter. - -Flying-wire construction is just fine for this low-frequency circuit. In a high-speed photodiode preamp, the -transimpedance amplifier circuit would be highly sensitive to stray capacitance, but we're not aiming at high speed -here. - -.. raw:: html - - <div class="subfigure"> - <figure> - <img src="images/preamp_front.jpg"> - <figcaption>The front side of the preamplifier board.</figcaption> - </figure> - <figure> - <img src="images/preamp_back.jpg"> - <figcaption>The wiring of the photodiode preamp.</figcaption> - </figure> - </div> - -Given a way to measure intensity what remains missing is a way to scan a single photodiode across the spectrum. - -Scanning the projection -~~~~~~~~~~~~~~~~~~~~~~~ - -A cheap linear stage can be found in any old CD or DVD drive. These drives use a small linear stage based on a -stepper-driven screw to move the laser unit radially. Removing the laser unit and connecting a leftover stepper driver -module I was left with a small linear stage with about 45 steps per cm without microstepping enabled. The driver I used -was an `A4988`_ module that required at least 8V motor drive voltage. I used a small micro USB-input boost converter -module to generate a stable 10V supply for the motor driver, with the USB's 5V rail used as a logic supply for the motor -driver. - -The `SFH2701`_ can easily be mounted to the linear stage using a small SMD breakout board glued in place with thin wires -connecting it to the transimpedance amplifier. The DVD drive linear stage is not very strong so it is important that -this wire does not put too much strain on it. - -Above the photodiode, I mounted a small piece of paper on the linear stage to be used as a projection screen to align -the linear stage in front of the spectrometer viewing window. A line on the screen paper points to the photodiode die in -parallel to the linear stage allowing precise alignment. - -The whole unit with photodiode preamplifier, linear stage, photodiode and stepper motor driver finally looks like this: - -.. raw:: html - - <figure> - <img src="images/electronics_whole.jpg" alt="The complete electronics setup of the spectrograph. In the back - there is the DVD drive stepper stage. In front of it, mounted on a piece of wood are a small USB-to-12V - switching-regulator module to power the stepper motor in the top left, below on the bottom left is the - photodiode preamp and on the right is a breadboard with the stepper driver module and lots of jumper wires - interconnecting everything. On the right of the breadboard, a buspirate is attached to interface everything to a - computer. On the bottom edge of the piece of wood, two LED panel meters are mounted for readout of the preamp - output and the stepper supply voltages."> - <figcaption>The complete electronics setup. The buspirate on the right interfaces to a computer and controls the - stepper driver and ADC'es the preamp output. The two panel meters show the preamp output and stepper voltage for - setup.</figcaption> - </figure> - -The projection of the spectrum can be adjusted by moving the light source relative to the entry slot and by moving -around the grating DVD. - -The capture process -~~~~~~~~~~~~~~~~~~~ - -To capture a spectrum, first the light source has to be mounted near the spectrograph's entry slot. The LED tape I -tested I just taped face-down directly into it. Next, the grating DVD has to be adjusted to make sure the spectrum -covers a sensible part of the photodiode's path. Mostly, this boils down to adjusting the photodiode distance and height -to match the vertical extent and wiggling the grating DVD to adjust the projection's horizontal position. - -After the optics are set-up, the photodiode preamplifier has to be adjusted. In my experiments, most LED tape at 5GΩ -required a high-ish amplification. The goal in this step is to maximize the peak response of the preamp to be just -shy of its VCC rail to make best use of its dynamic range. To adjust the pre-amp, I took several very coarsely-spaced -measurements to give me an estimate of the peak while I did not yet know its precise location. - -Since stray daylight totally swamped out the weak projection of the LED's spectrum I shielded the entire setup with a -small box made of black cardboard and two black t-shirts on top. This shielding proved adequate for all my measurements -but I had to be careful not to accidentially move the DVD that was stuck into the spectrograph with the shielding -t-shirts. - -For capturing a single spectrum I wrote a small python script that will automatically move the stepper in adjustable -intervals and take two measurements at each point, one with the LED tape off that can be used for offset calibration and -one with the LED tape on. All measurements are stored in a sqlite database that can then be accesssed from other -scripts. - -I built a small script that shows the progress of the current run and an jupyter notebook for data analysis. The jupyter -notebook is capable of live-updating a graph with the in-progress spectrum's data. This was quite useful as a sanity -check for when I made some mistake easy to spot in the resulting data. - -After one color channel is captured, the LED tape has to be manually set to the next color and the next measurement can -begin. - -.. raw:: html - - <figure> - <img src="images/raw_plot_cheap_rgb.svg" alt="A plot with three wide peaks, two large peaks on both sides and - one smaller one in the middle. The middle one overlaps the two on the sides. The large ones are about 2.5V in - amplitude. Overall, the plot is about 300 stepper steps wide with each peak being around 130 steps wide."> - <figcaption>A plot of the raw preamp output voltage versus stepper position. From left to right, the three peaks - are blue, green and red. Step 0 corresponds to the bottommost stepper position and the shortest wavelength. - </figcaption> - </figure> - - -Data analysis -~~~~~~~~~~~~~ - -Data analysis consists of three major steps: Offset- and stray light removal, wavelength and amplitude calibration and -color space mapping. - -Offset removal -************** -The first task is to remove the offset caused by dark current as well as stray light of the LED's bright primary -reflection on the DVD. The LED is very bright and only a small part of its light gets reflected by the grating towards -the photodiode screen. The remaining part of the light is reflected onto the table in front of the DVD spectrograph. -Though I covered all of this with black cardboard, some of that light ultimately gets reflected onto the photodiode. -This causes a large offset, in particular in the blue part of the spectrum since in this part the photodiode is closest -to the spectrograph's opening. - -The composite offset can be approximated with a second-order polynomial that is fitted to all the data outside of the -main peak's area. Since at this point the wavelength of each data point is still unknown this is done with a rough first -estimate of the three colors' peaks' locations and widths. - -Wavelength- and amplitude calibration -************************************* -The photodiode's response is strongly wavelength-dependent. In particular in the blue band, the photodiode's sensitivity -gets very poor down to about 20% at the edge to ultraviolet. This effect is strong enough to move the apparent location -of the blue peak towards red. - -.. raw:: html - - <figure> - <img src="images/photodiode_sensitivity.svg" alt="A plot of photodiode sensitivity against wavelength relative - to peak sensitivity at 820nm. The sensitivity rises from 20% at 380nm approximately linearly to 80% at 620nm, - then the rise rolls off."> - <figcaption>A plot of the photodiode's relative sensitivity in the visible spectrum. The sensitivity is - normalized against its peak at 820nm. - </figcaption> - </figure> - -The problem is that in order to remove this non-linearity, we would already have to know the wavelength of the measured -light. Since I don't, I settled for a two-step process. First, a coarse wavelength calibration is done relative to the -red peak and the short-wavelength edge of the blue peak. The photodiode measurements are then sensitivity-corrected -using this coarse measurement. Then all three channel peaks are measured in the resulting data and a fine wavelength -estimate is produced by a least-squares fit of a linear function. This fine estimate is then used for a second -sensitivity correction of all original measurements and the scale is changed from stepper motor step count to -wavelength in nanometers. - -.. raw:: html - - <figure> - <img src="images/processed_plot_cheap_rgb.svg" alt="A plot with three wide peaks, all three of different - heights. The leftmost peak is highest at 6nA, the middle peak lowest at 1.6nA and the rightmost peak in between - at 4nA. The middle one overlaps the two on the sides. Overall, the plot spans about 300nm on its x axis with - each peak being around 100nm wide."> - <figcaption>A plot of the processed measurements. From left to right, the three peaks are blue, green and red. - </figcaption> - </figure> - -.. FIXME re-do these measurements, avoiding clipping -.. FIXME re-do calibration using CCFL -.. FIXME calibration for brightness imbalance due to wedge-shaped projection of spectrum - -Color space mapping -******************* -Finally, to achieve the objective of measuring the LED tape's channels' precise color coordinates the measured spetra -have to be matched against the color spaces' *color matching functions*. The color matching functions describe how -strong the color space's idealized *standard observer* would react to light at a particular wavelength. Going from a -measured spectrum to color coordinates XYZ works by integrating over the product of the measurement and each color -coordinate's color matching function. - -The result are three color coordinates X, Y and Z for each channel R, G and B yielding nine coordinates in total. When -written as a matrix conversion between XYZ color space and LED-RGB color space is as simple as multiplying that matrix -(or its inverse) and a vector from one of the color spaces. - -In XYZ space, the set of colors that can be produced with this LED tape is described by the `parallelepiped`_ spanned by -the three channel's XYZ vectors. In the following figures, you can see a three-dimensional model of the RGB LED's color -space (colorful) as well as sRGB (white) for comparison plotted within CIE 1931 XYZ. There is no natural map to scale -both so for this illustration the LED color space has been scaled to fit. These figures were made with blender and a few -lines of python. The blender project file including all settings and the python script to generate the color space -models can be found in the `project repo`_. - -.. raw:: html - - <figure> - <video controls loop> - <source src="video/led_within_srgb_scale=1.0.mkv" type="video/h264"> - <source src="video/led_within_srgb_scale=1.0.webm" type="video/webm"> - Your browser does not support the HTML5 video tag. - </video> - <figcaption>Illustration of the measured LED color space scaled to fit within XYZ with sRGB (light gray) for - comparison. The thick, white line is the spectral locus. - - <a href="video/led_within_srgb_scale=1.0.mkv">mkv/h264 download</a> / - <a href="video/led_within_srgb_scale=1.0.webm">webm download</a> - </figcaption> - </figure> - -As you can see, the result is pretty disappointing. The LED's color space parallepiped is very narrow, which is because -the blue channel is much brighter than the other two channels. An easy fix for this is to scale-up the RGB space and -drop any values outside XYZ. The scaling factor is a trade-off between color space coverage and brightness. You can -produce the most colors when you clip all channels to brightness of the weakest channel (green in this case), but that -will make the result very dim. Scaling brightness like that stretches the RGB parallelepiped along its major axis. Up to -a point the number of possible colors (the gamut) increases at expense of maximum brightness. When the parallelepiped is -stretched far enought for all three channel vectors to be outside the 1,1,1 XYZ-cube, maximum brightness continues to -decrease but the gamut stays constant. I don't know a simple scientific way to solve this problem, so I just played -around with a couple of factors and settled on 2.5 as a reasonable compromise. Below is an illustration. - -.. raw:: html - - <figure> - <video controls loop> - <source src="video/led_within_srgb_fancy_camera_path_scale=2.5.mkv" type="video/h264"> - <source src="video/led_within_srgb_fancy_camera_path_scale=2.5.webm" type="video/webm"> - Your browser does not support the HTML5 video tag. - </video> - <figcaption>Illustration of the measured LED color space at scale factor 2.5 within XYZ with sRGB (light gray) - for comparison. The thick, white line is the spectral locus. - - <a href="video/led_within_srgb_fancy_camera_path_scale=2.5.mkv">mkv/h264 download</a> / - <a href="video/led_within_srgb_fancy_camera_path_scale=2.5.webm">webm download</a> - </figcaption> - </figure> - -Firmware implementation ------------------------ -In the end, the above measurements yield two matrices: One for mapping XYZ to RGB, and one for mapping RGB to XYZ. Of -the several versions of CIE XYZ I chose the CIE 1931 XYZ color space as a basis for the firmware because it is most -popular. Mapping a color coordinate in one color space to the other is as simple as performing nine floating-point -multiplications and six additions. Mapping Lab or Lch to RGB is done by first mapping Lab/Lch to XYZ, then XYZ to RGB. -Lab to XYZ is somewhat complex since it requires a floating-point power for gamma correction, but any self-respecting -libc will have one of those so this is still no problem. Lch also requires floating-point sine and cosine functions, but -these should still be no problem on most hardware. - -My implementation of these conversions in the ESP8266 firmware of my `Wifi LED driver`_ can be found `on Github`_. You -can view the Jupyter notebook most of the analysis above `here <http://nbviewer.jupyter.org/github/jaseg/led_drv/blob/master/doc/Spectrum%20Measurement.ipynb>`__. - -.. _`on Github`: https://github.com/jaseg/esp_led_drv/blob/master/user/led_controller.c -.. _`project repo`: https://github.com/jaseg/led_drv -.. _`Wifi LED driver`: {{<ref "blog/wifi-led-driver/index.rst">}} -.. _`small driver`: {{<ref "blog/wifi-led-driver/index.rst">}} -.. _`multichannel LED driver`: {{<ref "blog/multichannel-led-driver/index.rst">}} -.. _`sRGB`: https://en.wikipedia.org/wiki/SRGB -.. _`CC BY-SA 3.0`: https://creativecommons.org/licenses/by-sa/3.0 -.. _`Color spaces`: https://en.wikipedia.org/wiki/Color_space -.. _`HSV`: https://en.wikipedia.org/wiki/HSL_and_HSV -.. _`CIE Lab/LCh`: https://en.wikipedia.org/wiki/Lab_color_space -.. _`XYZ (CIE 1931)`: https://en.wikipedia.org/wiki/CIE_1931_color_space -.. _`camera obscura`: https://en.wikipedia.org/wiki/Pinhole_camera -.. _`article by two researchers from the National Science Museum in Tokyo`: http://www.candac.ca/candacweb/sites/default/files/BuildaSpectroscope.pdf -.. _`spectrograph`: https://en.wikipedia.org/wiki/Ultraviolet%E2%80%93visible_spectroscopy -.. _`monochromator`: https://en.wikipedia.org/wiki/Monochromator -.. _`diffraction grating`: https://en.wikipedia.org/wiki/Diffraction_grating -.. _`SFH2701`: https://dammedia.osram.info/media/resource/hires/osram-dam-2495903/SFH%202701.pdf -.. _`BPW34`: http://www.vishay.com/docs/81521/bpw34.pdf -.. _`transimpedance amplifier`: https://en.wikipedia.org/wiki/Transimpedance_amplifier -.. _`A4988`: https://www.pololu.com/file/0J450/A4988.pdf -.. _`parallelepiped`: https://en.wikipedia.org/wiki/Parallelepiped diff --git a/content/blog/multichannel-led-driver/index.rst b/content/blog/multichannel-led-driver/index.rst deleted file mode 100644 index 86fab72..0000000 --- a/content/blog/multichannel-led-driver/index.rst +++ /dev/null @@ -1,458 +0,0 @@ ---- -title: "32-Channel LED tape driver" -date: 2018-05-02T11:31:14+02:00 ---- - -Theoretical basics -================== - -Together, a friend and I outfitted the small staircase at Berlin's Chaos Computer Club with nice, shiny RGB-WW LED tape -for ambient lighting. This tape is like regular RGB tape but with an additional warm white channel, which makes for much -more natural pastels and whites. There are several variants of RGBW tape. Cheap ones have separate RGB and white LEDs, -which is fine for indirect lighting but does not work for direct lighting. Since we wanted to mount our tape in channels -at the front of the steps, we had to use the slightly more expensive variant with integrated RGBW LEDs. These are LEDs -in the 5050 (5.0mm by 5.0mm) form factor common with RGB LEDs that have a small section divided off for the white -channel. The red, green and blue LED chips sit together in the larger section covered with clear epoxy and the white -channel is made up from the usual blue LED inside a yellow phosphor in the smaller section. - -Since we wanted to light up all of 15 steps, and for greatest visual effect we would have liked to be able to control -each step individually we had to find a way to control 60 channels of LED tape with a reasonable amount of hardware. - -LED tape has integrated series resistors and runs off a fixed 12V or 24V constant-voltage supply. This means you don't -need a complex constant-current driver as you'd need with high-power LEDs. You can just hook up a section of LED tape -to a beefy MOSFET to control it. Traditionally, you would do *Pulse Width Modulation* (PWM) on the MOSFET's input to -control the LED tape's brightness. - -Pulse Width Modulation ----------------------- - -`Pulse Width Modulation`_ is a technique of controlling the brightness of a load such as an LED with a digital signal. -The basic idea is that if you turn the LED on and off much too fast for anyone to notice, you can control its power by -changing how long you turn it on versus how long you leave it off. - -PWM divides each second into a large number of periods. At the beginning of each period, you turn the LED on. After -that, you wait a certain time until you turn it off. Then, you wait for the next period to begin. The periods are always -the same length but you can set when you turn off the LED. If you turn it off right away, it's off almost all the time -and it looks like it's off to your eye. If you turn it off right at the end, it's on almost all the time and it looks -super bright to your eye. Now, if you turn it off halfway into the cycle, it's on half the time and it will look to your -eye as half as bright as before. This means that you can control the LED's brightness with only a digital signal and -good timing. - -.. raw:: html - - <figure> - <img src="images/pwm_schema.jpg" alt="A visualization of PWM at different duty cycles."> - <figcaption>Waveforms of two PWM cycles at different duty cycles.</figcaption> - </figure> - -PWM works great if you have a dedicated PWM output on your microcontroller. It's extremely simple in both hardware and -software. Unfortunately for us, controlling 32 channels with PWM is not that easy. Cheap microcontrollers only have `a -handful of hardware PWM outputs`_, so we'd either have to do everything in software, bit-banging our LED modulation, or -we'd have to use a dedicated chip. - -Doing PWM in software is both error-prone and slow. Since the maximum dynamic range of a PWM signal is limited by the -shortest duty cycle it can do, software PWM being slow means it has poor PWM resolution at maybe 8 bits at most. Poor -color resolution is not a problem if all you're doing is to fade around the `HSV rainbow`_, but for ambient lighting -where you *really* want to control the brightness down to a faint shimmer you need all the color resolution you can get. - -If you rule out software PWM, what remains are dedicated `hardware PWM controllers`_. Most of these have either of three -issues: - -* They're expensive -* They don't have generous PWM resolution either (12 bits if you're lucky) -* They're meant to drive small LEDs such as a 7-segment display directly and you can't just hook up a MOSFET to their - output - -This means we're stuck in a dilemma between two poor solutions if we'd want to do PWM. Luckily for us, PWM is not the -only modulation in town. - -.. _`Pulse Width Modulation`: https://en.wikipedia.org/wiki/Pulse-width_modulation -.. _`a handful of hardware PWM outputs`: https://www.nxp.com/parametricSearch#/&c=c731_c380_c173_c161_c163&page=1 -.. _`HSV rainbow`: https://en.wikipedia.org/wiki/HSL_and_HSV -.. _`hardware PWM controllers`: http://www.ti.com/lit/ds/symlink/tlc5940.pdf - -Binary Code Modulation ----------------------- - -PWM is the bread-and-butter of the maker crowd. Everyone and their cat is doing it and it works really well most of the -time. Unbeknownst to most of the maker crowd, there is however another popular modulation method that's mostly used in -professional LED systems: Enter `*Binary Code Modulation* (BCM) <http://www.batsocks.co.uk/readme/art_bcm_1.htm>`_. - -BCM is to PWM sort of what barcodes are to handwriting. While PWM is easy to understand and simple to implement if all -you have is a counter and an IO pin, BCM is more complicated. On the other hand, computers can do complicated and BCM -really shines in multi-channel applications. - -Similar to PWM, BCM works by turning on and off the LED in short periods fast enough to make your eye perceive it as -partially on all the time. In PWM the channel's brightness is linearly dependent on its duty cycle, i.e. the percentage -it is turned on. In PWM the duty cycle D is the total period T divided by the on period T_on. The issue with doing PWM -on many channels at once is that you have to turn off each channel at the exact time to match its duty cycle. -Controlling many IO pins at once with precise timing is really hard to do in software. - -BCM avoids this by further dividing each period into smaller periods which we'll call *bit periods* and splitting each -channel's duty cycle into chunks the size of these bit periods. The amazingly elegant thing in BCM now is that as you -can guess from the name these bit periods are weighted in powers of two. Say the shortest bit period lasts 1 -microsecond. Then the second-shortest bit period is 2 microseconds and the third is 4, the fifth 8, the sixth 16 and so -on. - -.. raw:: html - - <figure> - <img src="images/bcm_schema.jpg" alt="A visualization of BCM at different duty cycles."> - <figcaption>Waveforms of a single 4-bit BCM cycle at different duty cycles. This BCM can produce 16 different - levels.</figcaption> - </figure> - -Staggered like this, you turn on the LED for integer value of microseconds by turning it on in the bit periods -corresponding to the binary bits of that value. If I want my LED to light for 19 microseconds every period, I turn it on -in the 16 microsecond bit period, the 2 microsecond bit period and the 1 microsecond bit period and leave it off for the -4 and 8 mircosecond bit periods. - -Now, how this is better instead of just more complicated than plain old PWM might not be clear yet. But consider this: -Turning on and off a large number of channels, each at its own arbitrary time is hard because doing the timing in -software is hard. We can't use hardware timers since we only have two or three of those, and we have 32 channels. -However, we can use one hardware timer to trigger a really cheap external latch to turn on or off the 32 channels all at -once. With this setup, we can only controll all channels at once, but we can do so with very precise timing. - -All we need to do is to set our timer to the durations of the BCM bit periods, and we can get the same result as we'd -get with PWM with only one hardware timer and a bit of code that is not timing-critical anymore. - -Applications of Binary Code Modulation -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -BCM is a truly wondrous technique, and outside of hobbyist circles it is in fact very widely known. Though we're using -it to control just 32 channels here, you can do much more channels without any problems. The most common application -where BCM is invariably used is *any* kind of LED screen. Controlling the thousands and thousands of LEDs in an LED -screen with PWM with a dedicated timer for each LED would not be feasible. With BCM, all you need to dedicate to a -single LED is a flipflop (or part of one if you're multiplexing). In fact, there is a whole range of `ICs with no other -purpose than to enable BCM on large LED matrices <http://www.vabolis.lt/stuff/MBI5026.pdf>`_. Basically, these are a -high-speed shift register with latched outputs much like the venerable 74HC595_, only their outputs are constant-current -sinks made so that you can directly connect an LED to them. - -.. _74HC595: http://www.ti.com/lit/ds/symlink/sn74hc595.pdf - -Running BCM on LED tape -~~~~~~~~~~~~~~~~~~~~~~~ - -In our case, we don't need any special driver chips to control our LED tape. We just connect the outputs of a 74HC595_ -shift register to one MOSFET_ each, and then we directly connect the LED tape to these MOSFETs. The MOSFETs allow us to -drive a couple of amps into the LED tape from the weak outputs of the shift register. - -The BCM timing is done by hooking up two timer channels of our microcontroller to the shift registers *strobe* and -*reset* inputs. We set the timer to PWM mode so we can generate pulses with precise timing. At the beginning of each -bit period, a pulse will strobe the data for this bit period that we shifted in previously. At the end of the bit -period, one pulse will reset the shift register and one will strobe the freshly-reset zeros into the outputs. - -.. raw:: html - - <figure> - <img src="images/olsndot_output_schematic.jpg" alt="From left to right, we see the STM32, one of the shift - registers, and the LEDs and MOSFETs. The LED tape is driven to ground by the MOSFETs, which are in turn directly - driven from the shift register outputs. The shift register is wired up to the STM32 with its clock and data - inputs on SCK and MOSI and its RESET and STROBE inputs on channel 2 and 3 of timer 1."> - <figcaption> - The schematic of a single output of this LED driver. Multiple shift register stages can be cascaded. - </figcaption> - </figure> - - -Our implementation of this system runs on an STM32F030F4P6_, the smallest, cheapest ARM microcontroller you can get from -ST. This microcontroller has only 16kB of flash and 1kB of RAM, but that's plenty for our use. We use its SPI controller -to feed the modulation data to the shift registers really fast, and we use two timer channels to control the shift -registers' reset and strobe. - -We can easily cascade shift registers without any ill side-effects, and even hundreds of channels should be no problem -for this setup. The only reason we chose to stick to a 32-channel board is the mechanics of it. We thought it would be -easier to have several small boards instead of having one huge board with loads of connectors and cables coming off it. - -The BOM cost per channel for our system is 3ct for a reasonable MOSFET, about 1ct for one eighth of a shift register -plus less than a cent for one resistor between shift register and MOSFET. In the end, the connectors are more expensive -than the driving circuitry. - -.. _MOSFET: https://en.wikipedia.org/wiki/MOSFET -.. _STM32F030F4P6: http://www.st.com/resource/en/datasheet/stm32f030f4.pdf - -Hardware design -=============== - -From this starting point, we made a very prototype-y hardware design for a 32-channel 12V LED tape driver. The design is -based on the STM32F030F4P6_ driving the shift registers as explained above. The system is controlled through an RS485_ -bus that is connected up to the microcontroller's UART using an MAX485_-compatible RS485 transceiver. The LED tape is -connected using 9-pin SUB-D_ connectors since they are cheap and good enough for the small current of our short segments -of LED tape. The MOSFETs we use are small SOT-23_ logic-level MOSFETs. In various prototypes we used both International -Rectifier's IRLML6244_ as well as Alpha & Omega Semiconductor's AO3400_. Both are good up to about 30V/5A. Since we're -only driving about 2m of LED tape per channel we're not going above about 0.5A and the MOSFETs don't even get warm. - -.. _RS485: https://en.wikipedia.org/wiki/RS-485 -.. _MAX485: https://datasheets.maximintegrated.com/en/ds/MAX1487-MAX491.pdf -.. _IRLML6244: https://www.infineon.com/dgdl/?fileId=5546d462533600a4015356686fed261f -.. _AO3400: http://aosmd.com/pdfs/datasheet/AO3400.pdf -.. _SUB-D: https://en.wikipedia.org/wiki/D-subminiature -.. _SOT-23: http://www.nxp.com/documents/outline_drawing/SOT23.pdf - -Switching nonlinearities ------------------------- -During testing of our initial prototype, we noticed that the brightness seemed to jump around when fading to very low -values. It turned out that our extremely simple LED driving circuit consisting of only the shift register directly -driving a MOSFET, which in turn directly drives the LED tape was maybe a little bit too simple. After some measurements -it turned out that we were looking at about 6Vpp of ringing on the driver's output voltage. The picture below is the -voltrage we saw on our oscilloscope on the LED tape. - -.. raw:: html - - <figure> - <img src="images/driver_ringing_strong.jpg" alt="Strong ringing on the LED voltage waveform edge at about - 100% overshoot during about 70% of the cycle time."> - <figcaption>Bad ringing on the LED output voltage caused by wiring inductance. Note that the effect on the - actual LED current is less bad than this looks since the LED's V/I curve is nonlinear.</figcaption> - </figure> - - -Dynamic switching behavior: Cause and Effect -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A bit of LTSpice_ action later we found that the inductance of the few metres of cable leading to the LED tape is the -likely culprit. The figure below is the schematic used for the simulations. - -.. raw:: html - - <figure> - <img src="images/driver_output_ltspice_schematic.jpg" alt="The LTSpice schematic of one output of the driver, - taking into account the shift register's output ESR and the wiring ESL."> - <figcaption>The schematic of the simulation in LTSpice</figcaption> - </figure> - -As tested, the driver does not include any per-output smoothing so the ~.5A transient on each BCM cycle hits the cable -in full. Combined with the cable inductance, this works out to a considerable lag of the rising edge of the LED -current, and bad ringing on its falling edge. Below is the voltage on the LED output from an LTSpice simulation of our -driver. - -.. raw:: html - - <figure> - <img src="images/overshoot_sim_r0.svg" alt="The result of the LTSpice simulation of our driver output. The LED - current shows similar ringing to what we measured using the oscilloscope. Interestingly, the gate voltage shows - strong ringing, too."> - <figcaption>The result of our LTSpice simulation. This simulation assumes 1µH of wiring inductance and 50Ω of - output impedance on the part of the shift register. The ringing at the gate visible in the gate voltage graph is - due to feed-through of the ringing at the output through the MOSFET's parasitic Cgd.</figcaption> - </figure> - -We were able to reduce the rining and limit the effect somewhat by putting a 220Ω series resistor in between the shift -register output and the MOSFET gate. This resistor forms an RC circuit with the MOSFET's nanofarad or two of gate -capacitance. The result of this is that the LED current passing the wire's ESL rises slightly more slowly and thus the -series inductance gets excited slightly less, and the overshoot decreases. Below is a picture of the waveform with the -damping resistor in place and a picture of our measurement for comparison. The resistor values don't agree perfectly -since the estimated ESL and stray capacitance of the wiring is probably way off. - -.. raw:: html - - <figure> - <img src="images/driver_ringing_weak.jpg" alt="Weak ringing on the LED voltage waveform edge at about 30% - overshoot during about 20% of the cycle time."> - <figcaption>Adding a resistor in front of the MOSFET gate to slow the transition damped the ringing somewhat, - but ultimately it cannot be eliminated entirely. Note how you can actually see the miller plateau on the - trailing edge of this signal. - </figcaption> - </figure> - -.. raw:: html - - <figure> - <img src="images/overshoot_sim_r100.svg" alt="The result of the LTSpice simulation of our driver output with an - extra 100 Ohms between shift register output and MOSFET gate. Similar to the oscilloscope measurement the - ringing is much reduced in its amplitude."> - <figcaption>The LTSpice simulation result with the same parameters as above but with an extra 100Ω between the - shfit register's output and the MOSFET's gate.</figcaption> - </figure> - -A side effect of this fix is that now the effective on-time of the LED tape is much longer than the duty cycle at the -shift register's output at very small duty cycles (1µs or less). This is caused by the MOSFET's `miller -plateau`_. For illustration, below is a graph of both the excitation waveform (the boxy line) and the resulting LED -current (the other ones) both without damping (top) and with 220Ω damping (bottom). As you can see the effective duty -cycle of the LED current is not at all equal to the 50% duty cycle of the excitation square wave. - -.. raw:: html - - <figure> - <img src="images/asymmetric_iled.svg" alt="The result of an LTSpice simulation of the LED duty cycle without and - with damping. Dampening widens the LED current waveform from 50% duty cycle with sharp edges to about 80% duty - cycle with soft edges."> - <figcaption>Simulated LED duty cycle with and without damping. The damping resistance used in this simulation - was 220Ω.</figcaption> - </figure> - -.. raw:: html - - <figure> - <img src="images/asymmetric_vgate.svg" alt="The gate voltages in the spice simulation above. The undamped - response shows sharp edges with the miller plateau being a barely noticeable step, but with strong ringing on - the trailing edge. The damped response shows RC-like slow-edges, but has wide miller plateaus on both edges - adding up to about 50% of the pulse width."> - <figcaption>The MOSFET gate voltage from the simulation in the figure above. You can clearly see how the miller - plateau (the horizontal part of the trace at about 1V) is getting much wider with added damped, and how the - resulting gate charge/discharge curve is not at all that of a capacitor anymore.</figcaption> - </figure> - - - -In conclusion, we have three major causes for our calculated LED brightness not matching reality: - -* Ringing of the equivalent series inductance of the wiring leading up to the LED tape -* Miller plateau lag -* The damping resistor and the MOSFET gate forming an RC filter that helps with wire ESL ringing but worsens the miller - plateau issue and deforms the LED current edges. - -Added up, these three effects yield a picture that agrees well with our simulations and measurements. The overall effect -is neglegible at long period durations (>10µs), but gets really bad at short period durations (<1µs). The effect is -non-linear, so correcting for it is not as simple as adding an offset. - -.. _LTSpice: http://www.analog.com/en/design-center/design-tools-and-calculators/ltspice-simulator.html -.. _`miller plateau`: https://www.vishay.com/docs/68214/turnonprocess.pdf - -Measuring LED tape brightness -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In order to correct for the nonlinearities mentioned above, we decided to implement a lookup table mapping BCM period to -actual timer setting. That is, each row of the table contains the actual period length we need to set the -microcontroller's timer to in order to get our intended brightness steps. - -To calibrate our driver, we needed a setup for reproducible measurement of the relative brightness of our LED tape at -different settings. Absolute brightness is not of interest to us as the eye can't perceive it. To perform the -calibration, the LED driver is set to enable each single BCM period in turn, i.e. brightness values 1, 2, 4, 8, 16 etc. - -The setup we used to measure the LED tape's brightness consists of a bunch of LED tape stuck into a tin can for -shielding against both stray light and electromagnetic interference and a photodiode looking at the LED tape. We used -the venerable BPW34_ photodiode in our setup as I had a bunch leftover from another project and because they are quite -sensitive owing to their physically large die area. - -.. raw:: html - - <figure> - <img src="images/linearization_setup.jpg" alt="The led measurement setup consists of several PCBs and a - breadboard linked with a bunch of wires and a big tin can to shield the LEDs and the photodiode. A large sub-D - connector is put into the top of the tin can as a feed-through for the LED tape's control signals and the - photodiode signal. In the background the control laptop is visible."> - <figcaption>The LED brighness measurement setup. The big tin can contains a bunch of LED tape and the - photodiode. The breadboard on the right is used for the photodiode preamplifier and for jumpering around the LED - tape's channels. The red board next to it is the buspirate used as ADC. The board on the bottom left is a - TTL-to-RS485 converter and the board in the middle is the unit under test.</figcaption> - </figure> - -The photodiode's photocurrent is converted into a voltage using a very simple transimpedance amplifier based around a -MCP6002_ opamp that was damped into oblivion with a couple nanofarads of capacitance in its feedback loop. The MCP6002_ -is a fine choice here since I had a bunch and because it is a CMOS opamp, meaning it has low bias current that would -mess up our measurements. For many applications, opamp bias current is not a big issue but when using the opamp to -directly measure very small currents at its input it quickly swamps out the signal for most BJT-input types. - -The transimpedance amplifier's output is read from the computer using the ADC input of a buspirate USB thinggamajob. In -general I would not recommend the buspirate as a tool for this job since it's ADC is not particularly good and it's -programming interface is positively atrocious, but it was what I had and it beat first wiring up one of the dedicated -ADC chips I had in my parts bin. - -The computer runs a small python script cycling the LED tape through all its BCM period settings and taking a brightness -measurement at each step. Later on, these measurements can be plotted to visualize the resulting slope's linearity, and -we can even do a simulation of the resulting brightness for all possible control values by just adding the measured -photocurrents for a certain BCM setpoint just as our retinas would do. - -.. raw:: html - - <figure> - <img src="images/driver_linearity_raw.svg" alt=""> - <figcaption> - A plot of the measured brightness of our LED tape for each BCM period. The brightness values are normalized - to the value measured at the LSB setpoint (brightness=1/65535). Ideally, this plot would show a straight - line with slope 1. Obviously, it doesn't. The bend in the curve is caused by the above-mentioned duty cycle - offset adding an offset to all brightness values. Shown is both the raw data (light), which has essentially zero - measurement error and a linear fit (dark). - - The plot is in log-log to approximate how the human eye would perceive brightness, i.e. highly sensitive at - low values but not very sensitive at all at large values. - </figcaption> - </figure> - -While it would be possible to fully automate the optimization of BCM driver lookup tables, we needed only one and in the -end I just sat down and manually tweaked the ideal values we initially calculated until I liked the result. You can see -the resulting brightness curve below. - -.. raw:: html - - <div class="subfigure"> - <figure> - <img src="images/uncorrected_brightness_sim.svg" alt=""> - <figcaption> - Calculated brightness curve for the uncorrected BCM setup. As you can see, at low setpoints the result - is about as smooth as sandpaper, which is well in line with our observations. At high setpoints the - offset gets swamped out and the nonlinearity in the low bits is not visible anymore. - </figcaption> - </figure> - <figure> - <img src="images/corrected_brightness_sim.svg" alt=""> - <figcaption> - Brightness curve for the corrected BCM setup extrapolated using actual measurements. Looks as buttery - smooth in real life as it does in this plot. - </figcaption> - </figcaption> - </figure> - </div> - -.. _BPW34: http://www.vishay.com/docs/81521/bpw34.pdf -.. _MCP6002: http://ww1.microchip.com/downloads/en/DeviceDoc/21733j.pdf - -Controlling the driver ----------------------- - -Now that our driver was behaving linear enough that you couldn't see it actually wasn't we needed a nice way to control -it from a computer of our choice. In the ultimate application (our staircase) we'll use a raspberry pi for this. Since -we already settled on an RS485_ bus for its robustness and simplicity, we had to device a protocol to control the driver -over this bus. Here, we settled on a simple, COBS_-based protocol for the reasons I wrote about in `How to talk to your -microcontroller over serial <serial-protocols>`_. - -To address our driver nodes, we modified the Makefile to build a random 32-bit MAC into each firmware image. The -protocol has only five message types: - -1. A 0-byte *ping* packet, to which each node would reply with its own address in the - first 100ms after boot. This can be used to initially discover the addresses of all nodes connected to the bus. You'd - spam the bus with *ping* packets, and then hit reset on each node in turn. The control computer would then receive - each device's MAC address as you hit reset. -2. A 4-byte *address* packet that says which device that the following packet is for. This way of us using the packet - length instead of a packet type field is not particularly elegant, but our system is simple enough and it was easy to - implement. -3. A 64-byte *frame buffer* packet that contains 16 bits of left-aligned brightness data for every channel -4. A one-byte *get status* packet that tells the device to respond with... -5. ...a 27-byte status packet containing a brief description of the firmware (version number, channel count, bit depth - etc.) as well as the device's current life stats (VCC, temperature, uptime, UART frame errors etc.). - -Wrapped up in a nice python interface we can now easily enumerate any drivers we connect to a bus, query their status -and control their outputs. - -.. _COBS: https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing - -Conclusion ----------- - -.. raw:: html - - <div class="subfigure"> - <figure> - <a href="images/olsndot_schematic.png"> - <img src="images/olsndot_schematic.png" alt="A picture of the LED driver schematic"> - </a> - <figcaption>The LED driver <a href="images/olsndot_schematic.png">schematic</a></figcaption> - </figure> - <figure> - <a href="images/olsndot_pcb.png"> - <img src="images/olsndot_pcb.png" alt="A picture of the LED driver PCB layout"> - </a> - <figcaption>The LED driver <a href="images/olsndot_pcb.png">PCB layout</a></figcaption> - </figure> - </div> - -Putting some thought into the control circuitry and software, you can easily control large numbers of channels of LEDs -using extremely inexpensive driving hardware without any compromises on dynamic range. The design we settled on can -drive 32 channels of LED tape with a dynamic range of 14bit at a BOM cost of below 10€. All it really takes is a couple -of shift registers and a mildly bored STM32 microcontroller. - -Get a PDF file of the schematic and PCB layout `here <olsndot_v02_schematics_and_pcb.pdf>`__ or download the CAD files -and the firmware sources `from github <https://github.com/jaseg/led_drv>`_. You can view the Jupyter notebook used to -analyze the brightness measurement data `here <http://nbviewer.jupyter.org/github/jaseg/led_drv/blob/master/doc/Run_analysis.ipynb>`__. - diff --git a/content/blog/private-contact-discovery/index.rst b/content/blog/private-contact-discovery/index.rst deleted file mode 100644 index 797de50..0000000 --- a/content/blog/private-contact-discovery/index.rst +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: "Private Contact Discovery" -date: 2019-06-22T10:30:00+08:00 ---- - -Private Contact Discovery -========================= - -Private Contact Discovery (PCD) is the formal name for the problem modern smartphone messenger applications have on -installation: Given a user's address book, find out which of their contacts also use the same messenger without the -messenger's servers learning anything about the user's address book. The widespread non-private way to do this is to -simply upload the user's address book to the app's operator's servers and do an SQL JOIN keyed on the phone number field -against the database of registered users. People have tried sprinkling some hashes over these phone numbers in an -attempt to improve privacy, but obviously running a brute-force preimage attack given a domain of maybe a few billion -valid inputs is not cryptographically hard. - -Private Contact Discovery can be phrased in terms of Private Set Intersection (PSI), the cryptographic problem of having -two parties holding one set each find the intersection of their sets without disclosing any other information. PSI has -been an active field of research for a while and already yielded useful results for some use cases. Alas, none of those -results were truly practical yet for usage in PCD in a typical messenger application. They would require too much CPU -time or too much data to be transferred. - -At USENIX Security 2019, Researchers from technical universities Graz and Darmstadt published a paper titled *Private -Contact Discovery at Scale* -(`eprint <https://eprint.iacr.org/2019/517>`__ | `PDF <https://eprint.iacr.org/2019/517.pdf>`__). -In this paper, they basically optimize the hell out of existing cryptographic solutions to private contact discovery, -jumping from a still-impractical state of the art right to practicality. Their scheme allows a client with 1k contacts -to run PCD against a server with 1B contacts in about 3s on a phone. The main disadvantage of their scheme is that it -requires the client to in advance download a compressed database of all users, that clocks in at about 1GB for 1B users. - -I found this paper very interesting for its immediate practical applicability. As an excuse to dig into the topic some -more, I gave a short presentation at my university lab's research seminar on this paper -(slides: `PDF <mori_semi_psi_talk.pdf>`__ | `ODP <mori_semi_psi_talk.odp>`__). - -Even if you're not working on secure communication systems on a day-to-day basis this paper might interest you. If -you're working with social account information of any kind I can highly recommend giving it a look. Not only might your -users benefit from improved privacy, but your company might be able to avoid a bunch of data protection and -accountability issues by simply not producing as much sensitive data in the first place. diff --git a/content/blog/serial-protocols/index.rst b/content/blog/serial-protocols/index.rst deleted file mode 100644 index 2f9bb2d..0000000 --- a/content/blog/serial-protocols/index.rst +++ /dev/null @@ -1,249 +0,0 @@ ---- -title: "How to talk to your microcontroller over serial" -date: 2018-05-19T08:09:46+02:00 ---- - -Scroll to the end for the `TL;DR <Conclusion_>`_. - -In this article I will give an overview on the protocols spoken on serial ports, highlighting common pitfalls. I will -summarize some points on how to design a serial protocol that is simple to implement and works reliably even under error -conditions. - -If you have done low-level microcontroller firmware you will regularly have had to stuff some data up a serial port to -another microcontroller or to a computer. In the age of USB, an old-school serial port is still the simplest and -quickest way to get communication to a control computer up and running. Integrating a ten thousand-line USB stack into -your firmware and writing the necessary low-level drivers on the host side might take days. Poking a few registers to -set up your UART to talk to an external hardware USB to serial converter is a matter of minutes. - -This simplicity is treacherous, though. Oftentimes, you start writing your serial protocol as needs arise. Things might -start harmless with something like ``SET_LED ON\n``, but as the code grows it is easy to end up in a hot mess of command -modes, protocol states that breaks under stress. The ways in which serial protocols break are manifold. The simplest one -is that at some point a character is mangled, leading to both ends of the conversation ending up in misaligned protocol -states. With a fragile protocol, you might end up in a state that is hard to recover from. In extreme cases, this leads -to code such as `this gem`_ performing some sort of arcane ritual to get back to some known state, and all just because -someone did not do their homework. Below we'll embark on a journey through the lands of protocol design, exploring the -facets of this deceptively simple problem. - -.. _`this gem`: https://github.com/juhasch/pyBusPirateLite/blob/dece35f6e421d4f6a007d1db98d148e2f2126ebb/pyBusPirateLite/base.py#L113 - -Text-based serial protocols -=========================== - -The first serial protocol you've likely written is a human-readable, text-based one. Text-based protocols have the big -advantage that you can just print them on a terminal and you can immediately see what's happening. In most cases you can -even type out the protocol with your bare hands, meaning that you don't really need a debugging tool beyond a serial -console. - -However, text-based protocols also have a number of disadvantages. Depending on your application, these might not matter -and in many cases a text-based protocol is the most sensible solution. But then, in some cases they might and it's good -to know when you hit one of them. - -Problems --------- - -Low information density -~~~~~~~~~~~~~~~~~~~~~~~ - -Generally, you won't be able to stuff much more than four or five bit of information down a serial port using a -single byte of a human-readable protocol. In many cases you will get much less. If you have 10 commands that are only -issued a couple times a second nobody cares that you spend maybe ten bytes per command on nice, verbose strings such as -``SET LED``. But if you're trying to squeeze a half-kilobyte framebuffer down the line you might start to notice the -difference between hex and base-64 encoding, and a binary protocol might really be more up to the job. - -Complex parsing code -~~~~~~~~~~~~~~~~~~~~ - -On the computer side of thing, with the whole phalanx of an operating system, the standard library of your programming -language of choice and for all intents and purposes unlimted CPU and memory resources to spare you can easily parse -anything spoken on a serial port in real time, even at a blazing fast full Megabaud. The microcontroller side however is -an entirely different beast. On a small microcontroller, printf_ alone will eat about half your flash. On most small -microcontrollers, you just won't get a regex library even though it would make parsing textual commands *so much -simpler*. Lacking these resources, you might end up hand-knitting a lot of low-level C code to do something seemingly -simple such as parsing ``set_channel (13, 1.1333)\n``. These issues have to be taken into account in the protocol design -from the beginning. For example, you don't really need matching parentheses, don't use them. - -Fragile protocol state -~~~~~~~~~~~~~~~~~~~~~~ - -Say you have a ``SET_DISPLAY`` command. Now say your display can display four lines of text. The obvious approach to this -is probably the SMTP_ or HTTP_ way of sending ``SET_DISPLAY\nThis is line 1\nThis is line 2\n\n``. This would certainly -work, but it is very fragile. With this protocol, you're in trouble if at any point the terminating second newline -character gets mangled (say, someone unplugs the cable, or the control computer reboots, or a cosmic ray hits something -and ``0x10 '\n'`` turns into ``0x50 'P'``). - -.. _SMTP: https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol -.. _HTTP: https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol - -Timeouts don't work -~~~~~~~~~~~~~~~~~~~ - -You might try to solve the problem of your protocol state machine tangling up with a timeout. "If I don't get a valid -command for more than 200ms I go back to default state." But consider the above example. Say, your control computer -sends a ``SET_DISPLAY`` command every 100ms. If in one of them the state machine tangles up, the parser hangs since the -timeout is never hit, because a new line of text is arriving every 100ms. - -Framing is hard -~~~~~~~~~~~~~~~ - -You might also try to drop the second newline and using a convention such as ``SET_DISPLAY`` is followed by two lines of -text, then commands resume.". This works as long as your display contents never look like commands. If you are only ever -displaying the same three messages on a character LCD that might work, but if you're displaying binary framebuffer -data you've lost. - -Solutions ---------- - -Keep the state machine simple -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In a text-based protocol, always use a single line of text to represent a single command. Don't do protocol states or -modes where you can toggle between different interpretations for a line. If you have to send human-readable text as part -of a command (such as ``SET_DISPLAY``), escape it so it doesn't contain any newlines. - -This way, you keep your protocol state machine simple. If at any time your serial trips and flips a bit or looses a byte -your protocol will recover on the next newline character, returning to its base state. - -Encode numbers in hex when possible -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Printing a number in hexadecimal is a very tidy operation, even on the smalest 8-bit microcontrollers. In contrast, -printing decimal requires both division and remainder in a loop which might get annoyingly code- and time-intensive on -large numbers (say a 32-bit int) and small microcontrollers. - -If you have to send fractional values, consider their precision. Instead of sending a 12 bit ADC result as a 32-bit -float formatted like ``0.176513671875`` sending ``0x2d3`` and dividing by 4096 on the host might be more sensible. If you -really have to communicate big floats and you can't take the overhead of including both printf_ and scanf_ you can -use hexadecimal floating point, which is basically ``hex((int)foo) + "." + hex((int)(65536*(foo - (int)foo)))`` for four -digits. You can also just hex-encode the binary IEEE-754_ representation of the float, sending ``hex(*(int *)&float)``. -Most programming languages will have a `simple, built-in means to parse this sort of thing -<https://docs.python.org/3.5/library/struct.html>`__. - -.. _printf: http://git.musl-libc.org/cgit/musl/tree/src/stdio/vfprintf.c -.. _scanf: http://git.musl-libc.org/cgit/musl/tree/src/stdio/vfscanf.c -.. _IEEE-754: https://en.wikipedia.org/wiki/IEEE_754 - -Escape multiline strings -~~~~~~~~~~~~~~~~~~~~~~~~ - -If you have to send arbitrary strings, escape special characters. This not only has the advantage of yielding a robust -protocol: It also ensures you can actually see everything that's going on when debugging. The string ``"\r\n"`` is easy to -distinguish from ``"\n"`` while your terminal emulator might not care. - -The simplest encoding to use is the C-style backslash encoding. Host-side, most languages will have a `built-in means of -escaping a string like that <https://docs.python.org/3.5/library/codecs.html#text-encodings>`__. - -Encoding binary data --------------------- - -For binary data, hex and base-64 are the most common encodings. Since hex is simpler to implement I'd go with it unless -I really need the 30% bandwidth improvement base-64 brings. - -Binary serial protocols -======================= - -In contrast to anything human-readable, binary protocols are generally more bandwidth-efficient and are easier to format -and parse. However, binary protocols come with their own version of the caveats we discussed for text-based protocols. - -The framing problem in binary protocols ---------------------------------------- - -The most basic problems with binary protocols as with text-based ones is framing, i.e. splitting up the continuous -serial data stream into discrete packets. The issue is that it is that you have to somehow mark boundaries between -frames. The simplest way would be to use some special character to delimit frames, but then any 8-bit character you -could choose could also occur within a frame. - -SLIP/PPP-like special character framing -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Some protocols solve this problem much like we have solved it above for strings in line-based protocols, by escaping any -occurence of the special delimiter character within frames. That is, if you want to use ``0x00`` as a delimiter, you would -encode a packet containing ``0xde 0xad 0x00 0xbe 0xef`` as something like ``0xde 0xad 0x01 0x02 0xbe 0xef``, replacing the -null byte with a magic sequence. This framing works, but is has one critical disadvantage: The length of the resulting -escaped data is dependent on the raw data, and in the worst case twice as long. In a raw packet consisting entirely of -null bytes, every byte must be escaped with two escape bytes. This means that in this case the packet length doubles, -and in this particular case we're even less efficient than base-64. - -Highly variable packet length is also bad since it makes it very hard to make any timing guarantees for our protocol. - -9-bit framing -~~~~~~~~~~~~~ - -A framing mode sometimes used is to configure the UARTs to transmit 9-bit characters and to use the 9th bit to designate -control characters. This works really well, and gives plenty of control characters to work with. The main problem with -this is that a 9-bit serial interface is highly nonstandard and you need UARTs on both ends that actually support this -mode. Another issue is that though more efficient than both delmitier-based and purely text-based protocols, it still -incurs an extra about 10% of bandwidth overhead. This is not a lot if all you're sending is a little command every now -and then, but if you're trying to push large amounts of data through your serial it's still bad. - -COBS -~~~~ - -Given the limitations of the two above-mentioned framing formats, we really want something better. The `Serial Line -Internet Protocol (SLIP)`_ as well as the `Point to Point Protocol (PPP)`_, standardized in 1988 and 1994 respectively, -both use escape sequences. This might come as a surprise, but humanity has actually still made significant technological -progress on protocols for 8-bit serial interfaces until the turn of the millennium. In 1999, `Consistent Overhead Byte -Stuffing (COBS)`_ (`wiki <https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing>`__) was published by a few -researchers from Apple Computer and Stanford University. As a reaction on the bandwidth doubling problem present in -PPP_, COBS *always* has an overhead of a single byte, no matter what or how long a packet's content is. - -COBS uses the null byte as a delimiter interleaves all the raw packet data and a `run-length encoding`_ of the non-zero -portions of the raw packet. That is, it prepends the number of bytes until the first zero byte to the packet, plus one. -Then it takes all the leading non-zero bytes of the packet, unmodified. Then, it again encodes the distance from the -first zero to the second zero, plus one. And then it takes the second non-zero run of bytes unmodified. And so on. At -the end, the packet is terminated with a zero byte. - -The result of this scheme is that the encoded packet does not contain any zero bytes, as every zero byte has been -replaced with the number of bytes until the next zero byte, plus one, and that can't be zero. Both formatter and parser -each have to keep a counter running to keep track of the distances between zero bytes. The first byte of the packet -initializes that counter and is dropped by the parser. After that, every encoded byte received results in one raw byte -parsed. - -While this might sound more complicated than the escaping explained above, the gains in predictability and efficiency -are worth it. An implementation of encoder and decoder should each be about ten lines of C or two lines of Python. A -minor asymmetry of the protocol is that while decoding can be done in-place, encoding either needs two passes or you -need to scan forward for the next null byte. - -.. _`Point to Point Protocol (PPP)`: https://en.wikipedia.org/wiki/Point-to-Point_Protocol -.. _PPP: https://en.wikipedia.org/wiki/Point-to-Point_Protocol -.. _`Serial Line Internet Protocol (SLIP)`: https://en.wikipedia.org/wiki/Serial_Line_Internet_Protocol -.. _`Consistent Overhead Byte Stuffing (COBS)`: http://www.stuartcheshire.org/papers/COBSforToN.pdf -.. _`Point-to-Point Protocol (PPP)`: https://en.wikipedia.org/wiki/Point-to-Point_Protocol -.. _`run-length encoding`: https://en.wikipedia.org/wiki/Run-length_encoding - -State machines and error recovery ---------------------------------- - -In binary protocols even more than in textual ones it is tempting to build complex state machines triggering actions on -a sequence of protocol packets. Please resist that temptation. As with textual protocols keeping the protocol state to -the minimum possible allows for a self-synchronizing protocol. A serial protocol should be designed such that if due to -a dropped packet or two both ends will naturally re-synchronize within another packet or two. A simple way of doing that -is to always transmit one semantic command per packet and to design these commands in the most idempotent_ way possible. -For example, when filling a framebuffer piece by piece, include the offset in each piece instead of keeping track of it -on the receiving side. - -.. _idempotent: https://en.wikipedia.org/wiki/Idempotence#Computer_science_meaning - -Conclusion -========== - -Here's your five-step guide to serial bliss: - -1. Unless you have super-special requirements, always use the slowest you can get away with from 9600Bd, 115200Bd or - 1MBd. 8N1 framing if you're talking to anything but another microcontroller on the same board. Using common values - like these makes it easier when you'll inevitably have to guess these at some point in the future ;) -2. If you're doing something simple and speed is not a particular concern, use a human-readable text-based protocol. Use - one command/reply per line, begin each line with some sort of command word and format numbers in hexadecimal. Bonus - points for the device replying to unknown commands with a human-readable status message and printing a brief protocol - overview on boot. -3. If you're doing something even slightly nontrivial or need moderate throughput (>1k commands per second or >20 byte of - data per command) use a COBS-based protocol. A good starting point is a ``[target MAC][command ID][command - arguments]`` packet format for multidrop busses. For single-drop you may decide to drop the MAC address. -4. Always include some sort of "status" command that prints life stats such as VCC, temperature, serial framing errors - and uptime. You'll need some sort of ping command anyway and that command might as well do something useful. -5. If at all possible, keep your protocol context-free across packets/lines. That is, a certain command should always be - self-contained, and no command should change the meaning of the next packet/line/command that is sent. This is really - important to allow for self-synchronization. If you really need to break up something into multiple commands, say you - want to set a large framebuffer in pieces, do it in a idempotent_ way: Instead of sending something like ``FRAMEBUFFER - INCOMING:\n[byte 0-16]\n[byte 17-32]\n[...]\nEND OF FRAME`` rather send ``FRAMEBUFFER DATA FOR OFFSET 0: [byte - 0-16]\nFRAMEBUFFER DATA FOR OFFSET 17: [byte 17-32]\n[...]\nSWAP BUFFERS\n``. - diff --git a/content/blog/sybil-resistance-identity/images/succulents.jpg b/content/blog/sybil-resistance-identity/images/succulents.jpg Binary files differdeleted file mode 100755 index 938bffd..0000000 --- a/content/blog/sybil-resistance-identity/images/succulents.jpg +++ /dev/null diff --git a/content/blog/sybil-resistance-identity/index-old.rst b/content/blog/sybil-resistance-identity/index-old.rst deleted file mode 100644 index 6f1bee3..0000000 --- a/content/blog/sybil-resistance-identity/index-old.rst +++ /dev/null @@ -1,244 +0,0 @@ ---- -title: "Theia Attack Resistance and Digital Identity" -date: 2020-09-09T15:00:00+02:00 ---- - -.. raw:: html - - <figure class="header"> - <img src="images/succulents.jpg"> - <figcaption>Photo by <a href="https://unsplash.com/@timbennettcreative">Tim Bennett</a> on <a href="https://unsplash.com/">Unsplash</a></figcaption> - </figure> - - -Theia in Cyberspace -=================== - -In informatics, the term *distributed system* is used to describe the aggregate behavior of a complex network made up of -individual computers. For decades, computer scientists to some success have been trying to figure out how exactly the -individual computers that make up such a distributed system need to be programmed for the resulting amalgamation to -behave in a predictable, maybe even a desirable way. Though seemingly simple on its surface, this problem has a -surprising depth to it that has yielded research questions for a whole field for several decades now. One particular -as-of-yet unsolved problem is resistance against *theia attacks* (or "sybil" attacks in older terminology). - - Named after the 1973 book by Flora Rheta Schreiber on dissociative identity disorder, a sybil attack is an - attack where one computer in a distributed system pretends to be multiple computers to gain an advantage. From your - author's standpoint, naming a type of computer security attack after a medical condition was an unfortunate choice. - For this reason this post uses the term *Theia attack* to refer to the same concept. Theia is a greek godess of light - and glitter and the name alludes to the attacker performing something alike an optical illusion, causing the attacked - to perceive multiple distinct images that in the end are all only reflections of the same attacker. - -The core insight of computer science research on theia attacks is that there cannot be any technological way of -preventing such an attack, and any practical countermeasure must be grounded in some authority or ground truth that is -external to the systems—bridging from technology to its social or political context. - -Looking around, we can see a parallel between this question ("which computer is a real computer?") and a social issue -that recently has been growing in importance: Just like computers can pretend to be other computers, they can also -pretend to be humans. As can humans. Be it within the context of election manipulation or down-to-earth astroturfing_ -the recurring issue is that in today's online communities, it is hard for an individual to tell who of their online -acquaintances are who they seem to be. Different platforms attempt different solutions to this problem, and all fail in -some way or another. Facebook employs good old snitching, turning people against each other and asking them "Do you know -this person?". Twitter is more laid-back and avoids this Stasi_ methodology in favor of requiring a working mobile phone -number from its subjects, essentially short-circuiting identity verification to the phone company's check of their -subscriber's national passport. - -.. the preceding is a simplified representation of these platform's practices. In particular facebook uses several - methods depending on the case. I think this abbreviated discussion should be ok for the sake of the argument. I am - not 100% certain on the accuracy on the accuracy of the statement though. Does fb still do the snitching thing? Is - twitter usually content with a phone number? - -Trusting Crypto-Anarchist Authorities -===================================== - -Beyond these centralistic solutions to the problem, crypto-anarchists and anarcho-capitalists have been brewing on some -interesting novel approaches to online identity based on *blockchain* distributed ledger technology. Distributed -ledgers are a distributed systems design pattern that yields a system that works like an append-only logbook. -Participants can create new entries in this logbook, but no one—neither the original author, nor other participants—can -retroactively change a logbook entry once it has been written. In the blockchain model, past entries are essentially -written into stone. This near-perfect immutability is what opens them for a number of use cases from cryptographic -pseudo-currencies [#cryptocurrency]_. - -An overview over a variety of these unconventional blockchain identity verification approaches can be found in `this -unpublished 2020 survey by Siddarth, Ivliev, Siri and Berman <https://arxiv.org/ftp/arxiv/papers/2008/2008.05300.pdf>`_. -They walk their readers through a number of different projects that try to solve the question "Is this human who they -pretend to be?" using joint socio-technological approaches. In the following few sections, you may find a short outline -of a small selection of them. The conlusion of this post will be a commentary on these approaches, and on the underlying -problem of identity in a digital world. - -.. BrightID - -In one scheme, identity is determined by "notary" computers that aggregate large amounts of information on a user's -social contacts. These computers then run an algorithm derived from the SybilGuard_, SybilLimit_ and SybilInfer_ lineage -of random-walk based algorithms. These algorithms assume that authentic social graphs are small world graphs: Everyone -knows everyone else through a friend's friend's friend. They also assume that there is an upper bound on how many -connections with authentic users an attacker can forge: Anyone who is not embedded into the graph well enough is cut -out. Like this, they put an upper limit on the number of theia identites an attacker can assume given a certian number -of connections to real people. - -Disregarding the catastrophic privacy issues of storing large amounts of data on social relationships on someone else's -computer, this second assumption is where this model unfortunately breaks down. Applying common sense, it is completely -realistic for an attacker to forge a large number of social connections: This is precisely what most of social media -marketing is about! A more malicious angle on this would be to consider how in meatspace [#meatspacefn]_ multi-level -marketing schemes are successful in coaxing people to abuse their social graphs to disastrous consequences to the -well-being of themselves and others. Similar schemes would certainly be possible in cyberspace as well. An additional -point to consider is that the upper limit SybilGuard_ and others place on the number of fake identities one can have is -simply not that strict at all. An attacker could still get away with a reasonable number of false identities before -getting caught by any such algorithm. - -.. Duniter - -In another scheme, identity is awarded to anyone who can convince several people already in the network to vouch for -them, and who is at most a few degrees removed from one of several pre-determined celebrities. Apart from again being -vulnerable to conmen and other scammers, this system has the glaring flaw of roundly refusing to recognize any person -who is not willing or able to engage with multiple of its members. Along with the system's informal requirement for -members to only vouch for people they have physically met this leads to a nonstarter in a cyberspace that grown -specifically *because* it transcends national borders and physical distance—two most serious obstacles to in-person -communication. - -.. Idena Network - -The last scheme I will outline in this post is based around a set of `Turing tests`_; that is, quizzes that are designed -to tell apart man and machine. In this system, all participants have to simultaneously undergo a Turing test once in a -fortnight. The idea is that this limits the number of theia identities an attacker can assume since they can only solve -that many Turing tests at the same time. The system uses a particular type of picture classification-based Turing test -and does not seem to be designed with the blind or mentally disabled in mind with accessibility concerns nowhere to be -found in the so-called "manifesto" published by its creators. But even ignoring that, the system obviously fails at an -even more basic level: The idea that everyone takes a Turing test at the same time only works in a world without time -zones. Or jobs for that matter. Also, it assumes that an attacker cannot simply hire a small army of people someplace -else to fool the system. - -.. _SybilLimit: https://www.comp.nus.edu.sg/~yuhf/yuh-sybillimit.pdf -.. _SybilGuard: http://www.math.cmu.edu/~adf/research/SybilGuard.pdf -.. _SybilInfer: https://www.princeton.edu/~pmittal/publications/sybilinfer-ndss09.pdf -.. _`Turing Tests`: https://en.wikipedia.org/wiki/Turing_test - -Identity between Cyberspace and Meatspace -========================================= - -A common thread in these solutions, from the Facebook'esque Stasi_ methods to the crypto-anarchist challenge-response -utopias, is that they all approach digital identity as a question of Objective Truth™ that can unanimously be decided at -a system level—or that can be externalized to the next larger system such as the state. Alas, the important question -remains unasked: - - What *is* identity? - -The answer to this question certainly depends on the system being examined. For example, an important reason the -capitalist corporations mentioned above require knowledge about their users' identity is to generate plausible -statistics for the advertisers that form their customer base, similar to how a farmer will keep statics on yield and -quality for the buyers of his crop. With this background, a full decoupling of platform accounts from a notion of legal -identity seems at odds with the platform's business model—and we will have to adjust our expectations for reform -accordingly. - -A common thread among all systems mentioned above is that they all have a social component to them. For this common use -case of social systems, I want to make a suggestion on how we can approach digital identity in a more practical, less -discriminatory [#discriminatory]_ manner than any of the methods we discussed above. I think both using people's social -connections and proxying the decisions of external authorities such as the state are bad systems to decide who is a -person and who is not. I will now illustrate this point a bit. Let us think about how many digital identities a human -beign might have. First, consider the case of n=0, someone who simply wants no business with the system at all. For -simplicity, let us assume that we have solved this issue of consent, i.e. every person who is identified by the system -consents to this practice. For n=1, the approaches outlined above all provide some approximate solution. States may not -grant every human sufficient ID (e.g. children, the mentally disabled or prisoners might be left out), and the social -systems might fail to catch people who simply do not have any friends, but otherwise their approximations hold. Maybe. -But what about n=2, n=3, ...? None of these systems adequately consider cases where a human being might legitimately -wish to hold multiple digital identities, non-maliciously. - -Consider a hypothetical lesbian, conservative politician. An active social media presence is a core component of a -modern politician's carreer. At the same time, "conservative homophobe" is still well within the realm of tautology and -it would be legitimate for this politician to wish to not disclose a large fraction of their private life to the world -at large. They might have a separate online identity for matters related to it. For this politician, the social -relationship-based systems referenced above would either incorporate outing as a design feature, or they would force -the politician to choose either of their two identities: To choose between private life and carreer. When deferring to -the state as the decider over personhood, at least the platform's operator would know about the outrageously sensitive -link between the politician's online identities. Clearly, no such solution can be considered socially just. - -Let us try not to be caught up on saving the world at this point. The issue of conservative homophobia is out of the -scope of our consideration, and it is not one that anyone can solve in the near future. Magical realism aside, least of -all can some technological thing beckon this change. There is a case for legitimate uses of multiple, separate digital -identities, and we do not have a technical or political answer to it. All hope is not lost yet, though. We can easily -undo this gordian knot by acknowledging an unspoken assumption that underlies any social relationships between real -people, past the procrustean bed of computer systems or organizational structures these relationships are cast into. - - As a function of social interaction, digital identities conform to roles_ in sociological terminology, and are not - at all the same as personhood_. Roles are subjective and arise from a relationship between people, and a single - person might legitimately perform different roles depending on context. - -When computer scientists or programmers are creating new systems, there always is an (often implicit) modelling stage. -Formally, during this stage a domain expert and a modeller with a computer science background come together, each -contributing their knowledge to form a model that is both appropriate for real-world use and practical from an -engineering point of view. In practice, these two roles are often necessarily fulfilled by the same person, who is often -also the programmer of the thing. This leads to many computer systems using poor models. A typical example of this issue -are systems requiring a person's name that use three input fields labelled "First Name", "Middle Initial" and "Last -Name". These systems are often created by US-American programmers, who are used to this naming schema from their lived -experience. Unfortunately, this schema breaks down for those few billion people who use their last name first, who have -more than one middle name, or who have multiple given names and do not normally use the first one of those. - -Once a system creator's implicit assumptions have been encoded into the system like this, it is often very hard to get -out of that situation. A pattern to use during careful modelling is to keep the model flexible to account for unforeseen -corner cases. For example, when modelling a system requiring a person's name, one would have to ask what the name is -used for. It may be the most sensible decision to simply ask the user for their name twice: Once in first name/last name -format for e.g. tax purposes, and once with a free-form text field for e.g. displaying on their account page. - -While for names, many systems already use some form of flexible model by e.g. having a *handle* or *nickname* separate -from the *display name*, "social" systems still often are stuck with an identity model based around a concept of a -single, rigid identity. In practice, people perform different roles_ in different circumstances. When asking for a -person's identity, one would get wildly different answers from different people. A person's identity as perceived by -others is coupled to their relationship more than to some underlying, biological or administrative truth. Thinking back -to the straw man politician above, this is evident in subtle ways in almost all our everyday relationships: Some people -may know me by my legal name, some by my online nickname. To some I may be a computer scientist, to some a flatmate. -None of my friends and acquaintances have ever wanted to see my passport, or asked to take my DNA to ascertain that I am -a distinct human being from the other humans they know. Likewise, identifying me by my social connections is impractical -as it would require an exceedingly weird amount of what can only be described as snooping. Yet, this concept of a -single, consistent, global, true identity is exactly what up to now all technological solutions to the identity problem -are trying to achieve. - -Building Bridges -================ - -I think I can offer you one main take-aways from the discussion above. - - During modelling social systems, focus on relationships—not identity. - -Rephrased into more actionable points, as someone designing a social digital system, do the following: - -0. Early in the design stages, take the time to consider fundamental modelling issues like this one. If you don't, you - will likely get stuck with a sub-optimal model that will be hard to get rid of. -1. Where possible, be flexible. Allow people to chose their own identifier. Don't require them to use their real names, - they may not wish to disclose those or they may not be in a format that is useful to you (they may be too long, too - short, too ubiquituous, in foreign characters etc.). A free-form text field with a reasonable length limit is a good - approach here. -2. Do not use credit cards or phone numbers to identify people. There are many people who do not have either, and - scammers can simply buy this data in bulk on the darknet. -3. Allow people to create multiple identites [#accountswitchopsec]_, and acknowledge the role of social relationships in - your interaction features. People have very legitimate reasons to separate areas of their lifes, and it is not for - you or your computer to decide who is who to whom. If your thing requires a global search function, re-consider the - data protection aspects of your system. If you want to encourage social functions in the face of bots and trolls, - make it easy for people to share their identities out-of-band, such as through a QR code or a copy-and-pasteable - short link. If you require someone's legal name or address for billing purposes, unify these identities behind the - scenes if at all and allow them to act as if fully independent in public. - -While change of perspective comes with its share of user experience challenges, but also with a promise for a more -human, more dignified online experience. Perhaps we can find a way to adapt cyberspace to humans, instead of continuing -trying it the other way around. - -.. _astroturfing: https://en.wikipedia.org/wiki/Astroturfing -.. _Stasi: https://en.wikipedia.org/wiki/Stasi - -.. [#cryptocurrency] Pseudo-currencies in that, while they provide some aspects of a regular currency such as ownership - and transactions, they lack most others. Traditional currencies are backed by states, regulated by central banks - tasked with maintaining their stability and ultimately provide accountability through law enforcement, courts - and political elections. - -.. [#discriminatory] Discriminatory as in discriminating against minorities, but also as in deciding what is and what is - not. - -.. [#accountswitchopsec] This does mean that you should not actively prevent people from creating multiple accounts. It - does not necessarily entail building a proper user interface around this practice. If you do the latter, e.g. by - offering a "switch identity" button or an identiy drop-down menu on a post submission form, you can easily - encourage slip-ups that might disclose the connection between two identities, and you make it possible for - someone hacking a single login to learn about this connection as well. - -.. [#meatspacefn] Meatspace_ is where people physically are, as opposed to cyberspace - -.. _Meatspace: https://dictionary.cambridge.org/dictionary/english/meatspace -.. _roles: https://en.wikipedia.org/wiki/Role -.. _personhood: https://en.wikipedia.org/wiki/Personhood diff --git a/content/blog/sybil-resistance-identity/index.rst b/content/blog/sybil-resistance-identity/index.rst deleted file mode 100644 index f90b5ac..0000000 --- a/content/blog/sybil-resistance-identity/index.rst +++ /dev/null @@ -1,89 +0,0 @@ ---- -title: "Identity between Cyberspace and Meatspace" -date: 2020-09-09T15:00:00+02:00 -draft: true ---- - -.. raw:: html - - <figure class="header"> - <img src="images/succulents.jpg"> - <figcaption>Photo by <a href="https://unsplash.com/@timbennettcreative">Tim Bennett</a> on <a href="https://unsplash.com/">Unsplash</a></figcaption> - </figure> - -Identity in Cyberspace -====================== - -.. Identity is a frequent problem -.. Easy solutions abound -.. Precise modelling is uncommon -.. True identity is sensitive, hard to handle -.. -.. Often, conversational features emphasized -> true identity is unnecessary -.. Social role theory -.. Call to action - -Most computer systems that interface with humans have a concept of user identity. The data structures used for its -storage vary, but usually one *account* corresponds to one human *user*. In many applications, the system operator tries -to ensure that one user cannot create multiple accounts. In online social networks, astrotufing_ and trolling are easier -to fight when limits are imposed on account creation. In online stores, fraud prevention means the store operator needs -their customers legal identity and the operator must be able to ban offending customers. In mobile messaging systems, -users have to be able to find each other by some identifier such as name or phone number, and this identifier has to be -unique and hard to forge. - -Today, in systems that allow anyone to create an account have largely converged to require either an email address or a -mobile phone number. Email addresses are used by systems that are less vulnerable to abuse and that are used on laptop -or desktop computers. Mobile phone numbers are abundantly used in smartphone apps, as well as in systems more prone to -abuse such as online social networks or ecommerce. Both are easily verified using a confirmation email or SMS. - -When designing or programming an online system, it is uncommon that the precise real-world semantics of accounts are -modelled. Most computer systems use ad-hoc data models. During their creation, their programmers implicit assumptions -about the world are encoded into these data models. Most of the time this works fine, but it does lead to significant -blind spots that can make systems break down for a fraction of their users. - -Lives in Meatspace -================== - -A consequence of the proliferation of phone numbers being used to identify people is that most people will not be able -to create multiple accounts. *"That's the point!"* you might say, but while we want to prevent scammers, spammers and -boored schoolchildren from messing with our systems, everybody else may have legitimate reasons to have more than one -account. - -We can apply sociology's model of roles_ to understand this issue. In sociology, a role is the comprehensive pattern of -rules and expectations that govern an individual's behavior corresponding to their social position. A key fact is that -most people occupy mutliple roles. A parent may also be a company employee or a wife and perform accordingly given the -circumstances. Systems that tie digital identity to legal personhood through the contracts behind phone numbers impede -their users' attempts at role separation. Effects of this are e.g. that nowadays employers routinely screen applicants' -social media accounts for unacceptable content. - -While this role conflict merely amounts to a minor inconvenience to most there are many to who it poses an existential -problem. Consider an LGBT+ person living in a repressive country or a politically conservative person living in a -very liberal city. Both have legitimate reasons to strictly separate parts of their private lives from others. For both, -much is at stake. Yet, both will have to practically circumvent most online systems registration barriers to implement -this separation. - -Trusting the User -================= - -While there is no single solution to these issues, there are several possible mitigations. The first and most important -one is to systematically think about the system's data model when creating it. Which assumptions about the real world -are inherent in it? Are these assumptions likely to cause issues? Ad-hoc models are easily created, but hard to get rid -of when they start causing problems. - -A general guideline on identity should be that hindering trolls by requiring things like phone numbers or credit card -numbers is very likely to also be an obstacle to many entirely legitimate uses. Captchas_ or invitation links can help -to keep out the trolls. Another approach is to limit the damage a troll can cause with things like effective moderation -systems, reputation systems or by limiting the reach of newly created accounts. - -Outside of e-commerce, actually tying a digital account to a real-world identity is very rarely necessary. The value of -a messenger app is not in the names in its contacts list, but the conversations behind these names. When two people meet -each other on the street, their interaction is shaped by a myriad of social factors—but *not* by them showing each other -their photo ID. - -Humans with their messy identities do not fit today's cyberspace well. Let's adapt cyberspace to humans, instead of -trying it the other way around. - -.. _astroturfing: https://en.wikipedia.org/wiki/Astroturfing -.. _roles: https://en.wikipedia.org/wiki/Role -.. _Captchas: https://link.springer.com/content/pdf/10.1007/3-540-39200-9_18.pdf - diff --git a/content/blog/telekom-gpon-sfp/index.rst b/content/blog/telekom-gpon-sfp/index.rst deleted file mode 100644 index 17f78d5..0000000 --- a/content/blog/telekom-gpon-sfp/index.rst +++ /dev/null @@ -1,216 +0,0 @@ ---- -title: "Ubiquiti EdgeRouter on Deutsche Telekom GPON Fiber" -date: 2022-02-21T20:00:00+01:00 ---- - -Disclaimer -========== - -I provide this guide as a reference for other knowledgeable users without any warranty. Please feel free to use this as -a resource but do not hold me responsible if this does not work for you. There is a significant chance that due to an -error on my side or due to Telekom changing their setup this guide will not work for you, and you may end up having to -pay for an unsuccessful Telekom technician visit. That is your own risk, and I do not assume any liability. - -Tl;dr -===== - -The "Telekom Digitalisierungsbox Glasfasermodem" is a GPON ONT in SFP form factor that works with an Ubiquiti EdgeRouter -6P's SFP port. You can order it from Telekom or other vendors using the Telekom P/N 40823569 or its EAN 4718937619382. -It costs about the same as the separate plastic box modem, but saves a lot of space and does not require a separate -power supply. - -To configure, first access the SFP ONT's web interface at ``10.10.1.1`` by configuring your SPF port's IP to static -``10.10.1.2``. User credentials are either admin/admin or admin/1234. In the web interface, set put PLOAM password into the -"SLID" setting in ASCII mode, then save & reboot the device. Now, configure PPPoE on the router's SFP port using the -PPPoE UID ``[anschlusskennung] [zugangsnummer] "#" [mitbenutzernummer] "@t-online.de"`` and your "Persönliches Kennwort" as -PPPoE password. Set the VLAN to ``7``, and you are good to go. - -Background -========== - -I moved into a new apartment that has a fiber internet connection operated by Deutsche Telekom. Having made some poor -experiences with AVM's FritzBox brand of routers that is commonly used by German carriers, I decided to use my own -Router instead of the one provided by Deutsche Telekom. Like other German providers, Telekom charges exorbitant amounts -in monthly fees for their routers, so even though my choice ended up being a high-end piece of commercial equipment I -will still be cheaper than going with Telekom's much shittier device when added up over a two-year contract period. - -The hardware I chose is the Ubiquiti EdgeRouter 6P. This device is from Ubiquiti's commercial lineup and is intended to -power something like a small branch office of a company. It comes in a small form factor (as opposed to larger rackmount -units), it does not consume a lot of power, it has five PoE-capable Ethernet ports which I can directly connect up to -the Ubiquiti Unifi UAP access point that I already have, and it has a powerful configuration interface. It can even -act as a VPN endpoint! - -Telekom's fiber internet offering for residential customers is GPON-based. GPON stands for "Gigabit Passive Optical -Network" and means that instead of patching through one fiber or pair of fibers to each customer, several customers in -one building are connected to a single fiber through optical splitters. These optical splitters are passive, i.e. they -are just fancy pieces of glass and fibers and do not require electrical power. The advantage of GPON is lower initial -cost for the operator, the disadvantage is that competing providers can only ever hope to get traffic handed through by -Telekom and will never be able to use their own equipment on the "network" end of the fiber. - -Telekom wants you to connect to its fiber network through a small plastic box that they call "modem", and that the rest -of the world calls "ONT", or Optical Network Terminator. Telekom's ONT has an upstream optical port with an LC -connector, and a regular RJ45 ethernet port downstream. The "modem" in fact contains an entire linux system that -terminates the ITU-standard suite of protocols that is used to manage what happens on the fiber, e.g. scheduling of -transmission slots and adjustment of transmitter laser power. - -Looking at Telekom's plastic box ONT and my nice and shiny EdgeRouter, I was not a fan of this solution. Doing some -research I found out that you can in fact get GPON ONTs in an SFP module form factor. My EdgeRouter has an SFP slot, so -if I could get one of these that is compatible with Telekom's GPON flavor I could theoretically just plug it into my -EdgeRouter's SFP slot with no separate power supply needed, saving a lot of space in the process. - -Finding a GPON SFP ONT that is compatible with Telekom's network turned out to be the hard part. While there are lots of -commercial devices that look like they *should be* compatible, I could not be sure and I did not feel like sinking lots -of money and weeks of trial and error into figuring out which are and which are not. After about half a dozen calls with -various Telekom customer service departments I found the solution that ultimately ended up working: For their business -customer fiber internet offering, Telekom uses the same GPON standard, but different ONT equipment. Their router for -business customers is called "Digitalisierungsbox" and it in fact comes with an SFP GPON ONT. And, as it turns out, you -can order that SFP GPON ONT separately for about 50 € (the same as the plastic box one) from either Telekom or a number -of independent online stores. The Telekom part number of the thing is 40823569, the EAN is 4718937619382. - -Below is a list of steps that I had to undertake in order to get my EdgeRouter/SFP ONT setup to work. - -Hardware Setup -============== - -The hardware setup is really simple. The SFP ONU is plugged into the EdgeRouter's SFP port. The ONU is connected to -the Telekom Fiber through the LC/APC to SC/APC adapter cable that is included in its package. Telekom's technician will -install an LC/APC coupler to join both cables. To configure the EdgeRouter, connect yourself through an ethernet cable -*on port 2*. Ubiquiti's setup wizards assume the WAN interface is either port 1 or the SFP port (port 5), and default to -use port 2 as their LAN interface even when port 5 is configured as the only WAN port. The default IP for the EdgeRouter -is ``192.168.1.1``, and the default UID/PW is ubnt/ubnt. - -Configuration -============= - -Getting access to the SFP ONU's config interface ------------------------------------------------- - -In this section I am assuming you want to configure the SFP ONU while it is plugged into the EdgeRouter from a laptop -connected to the EdgeRouter's ethernet port 2. To do this, we have to first configure the right IP/subnet on the -EdgeRouter's SFP interface, then patch connections between the SFP ONU and the laptop through the EdgeRouter. - -1. First, inside the EdgeRouter's config interface we need to configure a static IP with accompanying SNAT rule on the - SFP port to allow us to access the SFP module's web interface through the laptop connected to the EdgeRouter. For - this, configure the eth5 interface (which is the SFP port) to use the static IP ``10.10.1.2/24``. - -.. raw:: html - - <figure style="width: 20em"> - <a href="images/edgerouter_sfp_config.png"> - <img src="images/edgerouter_sfp_config.png" alt="The EdgeRouter's graphical configuration interface showing IP - address 10.10.1.2/24 being configured for interface eth5, which is the SFP interface."> - </a> - <figcaption>SFP interface configuration to access the SFP ONU from a laptop connected to the EdgeRouter's LAN - port</figcaption> - </figure> - -2. With the SFP port assigned an IP address, we need to add a NAT rule to forward connections from the configuration - laptop on eth2 to the SFP port. We do this by adding a source NAT rule with masquerading enabled, for the TCP - protocol, with destination address ``10.10.1.0/24`` (the SFP config interface's private network). - -.. raw:: html - - <figure style="width: 20em"> - <a href="images/edgerouter_snat_config.png"> - <img src="images/edgerouter_snat_config.png" alt="The EdgeRouter's graphical configuration interface showing a - source NAT being configured for interface eth5 for TCP protocol connections to destination address 10.10.1.1 - using masquerading."> - </a> - <figcaption>Source NAT configuration to access the SFP ONU from LAN. eth5, masquerading on, TCP, destination - 10.10.1.1 (the SFP ONU's IP).</figcaption> - </figure> - -3. Finally, make sure that your laptop will actually use the EdgeRouter as its gateway for IPs within ``10.10.1.0/24``. - On the laptop, disable any VPNs, disconnect your Wifi and make sure that IP r shows a default route pointing at the - EdgeRouter's ``192.168.1.1``. If that isn't the case, on Linux you can manually add the necessary route by using - ``sudo ip r a 10.10.1.0/24 via 192.168.1.1 dev enp5s0`` - -After setting up this temporary route, you should be able to access the SFP ONU's configuration web interface by -pointing a browser at ``http://10.10.1.1/`` Just make sure you use plain-text HTTP here, not secure HTTP**S**. The -default login credentials for the device are admin/1234. - -.. raw:: html - - <figure style="width: 30em"> - <a href="images/sfp_onu_web_if.png"> - <img src="images/sfp_onu_web_if.png" alt="The SFP ONU configuration web interface is a basic-looking website with - a big Zyxel logo on it. It has menu options named status, setup and management. It shows a system overview - page that lists the device's uptime and software version."> - </a> - <figcaption>The SFP ONU's web interface.</figcaption> - </figure> - -Configuring the PLOAM password / SLID / ONT-Installationskennung ----------------------------------------------------------------- - -On the SFP ONU's web interface, we only have to change one single setting: Under "Setup", we have to set what the SFP -ONU calls "SLID" to the PLOAM password for the interface. Telekom calls this the "ONT-Installationskennung". You get -this from your Telekom technician. In the config interface, select ASCII mode and enter the number using the format -``ABCD000000`` with four capital letters followed by six zeros. If necessary, you can read the SFP ONU's serial number -on this page. - -.. raw:: html - - <figure style="width: 30em"> - <a href="images/sfp_onu_ploam_pw_config.png"> - <img src="images/sfp_onu_ploam_pw_config.png" alt="The SFP ONU configuration web interface shows its SLID - configuration page. A text field labelled SLID asks the user to enter a value of at most ten characters. As - an example, abcdefg123 is listed."> - </a> - <figcaption>The SFP ONU's config interface to set SLID/PLOAM PW/ONT-Installationskennung.</figcaption> - </figure> - -Press "Save Config" on the top right of the web page, then select "Reset ONU" and click "Apply" under the "Reset ONU" -link on the left. Make sure to not select the factory reset option instead. - -.. raw:: html - - <figure style="width: 30em"> - <a href="images/sfp_onu_reset.png"> - <img src="images/sfp_onu_reset.png" alt="The SFP ONU configuration web interface shows its reset ONU page. There - are two options labelled Reset ONU and Reset to factory default settings. The reset ONU option is - selected."> - </a> - <figcaption>Rebooting the SFP ONU.</figcaption> - </figure> - -With the ONU configured, after the reset the "GPON Information" page from the left menu under "Status" from the top menu -should show ``GPON Line Status: O5``. You can now remove the SNAT rule and IP address from the SFP interface in the -EdgeRouter's config. I recommend this since there is no way to change the ONU's default credentials, and leaving the -SNAT rule in place makes it vulnerable to attacks from your LAN. If you use the EdgeRouter's setup wizard in the next -step, that wizard will reset all of these settings. - -Configuring PPPoE and NAT -------------------------- - -Our ONU now has a low-level connection to Telekom's fiber network. The next step is to configure the EdgeRouter to -authenticate with the ONU through PPPoE. The easiest way to do this is to use the EdgeRouter's "Basic Setup" wizard as -described in the `EdgeOS User Guide`. In the wizard, select the SFP port (``eth5``) as the internet/WAN port. Select -``Internet Connection Type`` as ``PPPoE``, then enter the PPPoE credentials you got from your Telekom technician. The -password is your "Persönliches Kennwort" that you also use to log in to your customer account on Telekom's website. The -account name is ``[anschlusskennung] [zugangsnummer] "#" [mitbenutzernummer] "@t-online.de"``, so something like -``002712345678012345678901#0001@t-online.de``. Enable "Internet connection is on VLAN" and enter VLAN ID ``7``. This is -necessary because of the way Telekom set up their triple play (TV/phone/internet) service. After following through with -the wizard, your internet should be already working on port 2 of the router. Note that despite selecting the SFP port as -the router's WAN port, the wizard will still reserve port 1 (``eth0``) for another WAN interface, so you will only be -able to access the configuration interface through port 2 (``eth1``) after the wizard is done. You can of course change -this later. - -That's it, you're done and your internet should be working! - -Having Fun with the SPF GPON ONU -================================ - -If you want to dig deeper into the internals of Telekom's GPON implementation, the SFP ONU's firmware is a great -starting point. Default credentials are all admin/admin or admin/1234 and you can even get a regular busybox shell on -the device through SSH. The device's firmware is based on OpenWRT, and the source for large parts of the core control -components can be found under open source licenses as well. While I would strictly advice you to not mess around with -the actual modem settings because due to GPON you share a medium with your neighbors and might very well disrupt their -internet if you mess up, inspecting the ONU's firmware is a great way to learn about the inner workings of a modern GPON -network. - -If you are interested in messing around with the SFP ONU, there is a github repository where interesting thins are -collected `here <https://github.com/xvzf/zyxel-gpon-sfp/issues>`__. - -.. _`EdgeOS User Guide`: https://dl.ubnt.com/guides/edgemax/EdgeOS_UG.pdf - diff --git a/content/blog/thors-hammer/index.rst b/content/blog/thors-hammer/index.rst deleted file mode 100644 index ba851a5..0000000 --- a/content/blog/thors-hammer/index.rst +++ /dev/null @@ -1,60 +0,0 @@ ---- -title: "Thor's Hammer" -date: 2018-05-03T11:59:37+02:00 ---- - -In case you were having an inferiority complex because your friends' IBM Model M keyboards are so much louder than the -shitty rubber dome freebie you got with your pc... Here's the solution: Thor's Hammer, a simple typing cadence enhancer -for `PS/2`_ keyboards. - -.. raw:: html - - <figure> - <video controls loop> - <source src="video/thors_hammer.mov" type="video/h264"> - <source src="video/thors_hammer.webm" type="video/webm"> - Your browser does not support the HTML5 video tag. - </video> - <figcaption>A demonstration of the completed project. - - <a href="video/thors_hammer.mov">h264 download</a> / - <a href="video/thors_hammer.webm">webm download</a> - </figcaption> - </figure> - -The connects to the keyboard's PS/2 clock line and briefly actuates a large solenoid on each key press. An interesting -fact about PS/2 is that the clock line is only active as long as either the host computer or the input device actually -want to send data. In case of a keyboard that's the case when a key is pressed or when the host changes the keyboard's -LED state, otherwise the clock line is silent. We ignore the LED activity for now as it's generally coupled to key -presses. By just triggering an NE555 configured as astable flipflop we can stretch each train of clock pulses to a -pulse a few tens of milliseconds long that is enough to actuate the solenoid. - -.. raw:: html - - <figure> - <img src="images/thors_hammer_schematic.jpg" alt="The schematic of the PS2 driver"> - <figcaption>The schematic of the driver stretching the PS/2 clock pulses to drive the solenoid.</figcaption> - </figure> - - -Since PS/2 sends each key press and key release separately this circuit will pulse twice per keystroke. It would be -possible to ignore one of them but I figure the added noise just adds to the experience. - -Built on a breadboard, the circuit looks like this. - -.. raw:: html - - <figure> - <img src="images/thors_hammer_breadboard.jpg" alt="The circuit built on a breadboard"> - <figcaption>The completed circuit built up on a breadboard and attached to a keyboard.</figcaption> - </figure> - - -Since my solenoid did not have a tensioning spring I used a rubber band and some vinyl tape to make an adjustable -tensioner. The small orange USB hub serves as an end-stop because I had nothing else of the right shape. The sound and -resonance of the thing can be adjusted to taste by moving the end stop, adjusting the tensioning rubber and tuning the -excitation duration using the potentiometer. My particular solenoid was a bit slow so I added some pieces of circuit -board as shims between the plunger and the case to limit the plunger's travel inside the solenoid core. - -.. _`PS/2`: https://en.wikipedia.org/wiki/PS/2_port - diff --git a/content/blog/wifi-led-driver/index.rst b/content/blog/wifi-led-driver/index.rst deleted file mode 100644 index 1c4024a..0000000 --- a/content/blog/wifi-led-driver/index.rst +++ /dev/null @@ -1,146 +0,0 @@ ---- -title: "Wifi Led Driver" -date: 2018-05-02T11:31:03+02:00 ---- - -Project motivation -================== - -.. FIXME finished project picture with LED tape -.. raw:: html - - <figure> - <img src="images/board_in_case.small.jpg"> - <figcaption>The completed driver board installed in the 3D-printed case. This device can now be connected to - 12V and two segments of LED tape that can then be controlled trough Wifi. The ESP8266 module goes on the pin - header on the left and was removed for this picture. - </figcaption> - </figure> - -After the `multichannel LED driver`_ was completed, I was just getting used to controlling LEDs at 14-bit resolution. -I liked the board we designed in this project, but at 32 channels it was a bit large for most use cases. Sometimes I -just want to pop a piece of LED tape or two somewhere, but I don't need a full 32 channels of control. I ended up -thinking that a smaller version of the 32-channel driver that didn't require a separate control computer would be -handy. So I sat down and designed a variant of the design with only 8 channels instead of 32 and an on-board ESP8266_ -module instead of the RS485_ transceiver for WiFi connectivity. - -The Electronics -=============== - -The schematic was mostly copy-pasted from the 32-channel design. The PCB was designed from scratch. This time, I went -for a 5x7cm form factor to allow for enough room for all connectors and to give the ESP8266_'s WiFi antenna enough -space. The board has two 5-pin Phoenix-style_ for two RGB-White (RGBW) tapes and one 2-pin Phoenix-style_ connector for -12V power input. The control circuitry and the serial protocol are unchanged, but the STM32_ now talks to an ESP-01_ -module running custom firmware. - -The LEDs are driven using a 74HC595_ shift register controlling a bunch of AO3400_ MOSFETs_, with resistors in front of -the MOSFETs_' gates to slow down the transitions a bit to reduce brighntess nonlinearities and EMI_ resulting from -ringing of the LED tape's wiring inductance. - -The board has two spots for either `self-resettable fuses (polyfuses) <polyfuse_>`__ or regular melting-wire fuses_ in -a small SMD_ package, one for each RGBW output. For low currents the self-resettable fuses should be okay but at higher -currents their `trip times get long enough that they become unlikely to trip in time to save anything -<littlefuse-16r-datasheet_>`__, so plain old non-resettable fuses would be the way to go there. - -.. FIXME finished board photos -.. FIXME board with test tape picture - -.. raw:: html - - <div class="subfigure"> - <figure> - <img src="images/schematic.png"> - <figcaption> - The schematic of the driver board, with the ESP8266 on the top left, the STM32 microcontroller for LED - modulation below, the shift register in the middle and the LED drivers and outputs on the right. - <a href="resource/schematic_and_pcb.pdf">Download PDF</a> - </figcaption> - </figure> - <figure> - <img src="images/layout.png"> - <figcaption> - The board layout with the top side being visible. The top side contains the footprint for the ESP8266, the - microcontroller, fuses, filter cap, connectors and the shift register. The LEDs are connected on the left, - with one connector per LED tape segment. The power input connector is on the bottom right. The LED driver - MOSFETs are in small SOT-23 packages on the back of the board. Since this board is not intended for - super-high currents, the MOSFETs are adequately cooled just through the board's copper planes. - <a href="resource/schematic_and_pcb.pdf">Download PDF</a> - </figcaption> - </figure> - </div> - -.. raw:: html - - <figure> - <img src="images/boards.small.jpg"> - <figcaption>The completed PCBs of this project (front) and the `multichannel LED driver`_ project the driver - circuitry was derived from (back). - </figcaption> - </figure> - - -The Firmware -============ - -The STM32_ firmware only had to be slightly modified to accomodate the reduced channel count since the protocol remains -unchanged. The ESP firmware is based on esphttpd_ by Spritetm_. The modifications to the webserver firmware are pretty -basic. First, the UART console has been disabled since I use the UART to talk to the STM32. The few bootloader messages -popping out the UART on boot are not an issue, since they're unlikely to contain the fixed 32-bit address prefix the -serial protocol requires for the STM32_ to do anything. - -Second, I added LED control by adding drivers for the serial protocol and a bunch of colorspace conversion functions. -When I first tested the prototype software, I noticed that color reproduction was extremely poor. When I just sent a -HSV_ rainbow fade from a python command line, the result looked totally wrong. The fade did not seem to go at a constant -speed and some colors, in particular yellow, orange and greens, were not visible at all. The problem turned out to be a -stark mismatch of the red, green and blue channels of the LED tape and less-than-optimal color reproduction of the pure -colors. I decided to properly measure the LED tape's color reproduction so I could compensate for it in software. This -turned out to be an extremely interesting project, the details of which you can read in my `LED characterization`_ -article. - -Third, I updated the built-in websites with some ad-hoc documentation on how to use the thing and a basic interface for -LED control. - -.. FIXME screenshot of firmware website - -Making an enclosure -=================== - -To be actually useful, the driver needed a robust enclosure. Bare PCBs are nice for prototyping, but for actually -putting the thing anywhere it needs a case to protect it against random destruction. - -The board has four mounting holes with comfortable spacing in its corners to allow easy mounting inside a 3D-printed -case. The case itself is described in an OpenSCAD_ script. To make it look a little nicer, a little 3D relief is laid -into the lid. The 3D relief is generated with a bit of blender magic. The source STL_ model is loaded into blender, then -blender's amazingly flexible rendering system is used to export a depth map of a projection of the model as a PNG_ file. -This depth map is then imported as a triangle mesh into OpenSCAD_. - -For the relief to look good, I chose a rather high resolution for the depth map. This unfortunately leads to extreme -memory use and processing time on the part of OpenSCAD_, but since I have access to a sufficiently fast machine that is -not a problem. Just be careful if you try opening the OpenSCAD_ file on your machine, OpenSCAD_ will probably crash -unless you're on a beefy machine or interrupt it when it starts auto-rendering the file. - -The board is mounted into the enclosure using knurled insert nuts that are pressed into a 3D-printed hole using a bit of -violence. - -.. _`multichannel LED driver`: {{<ref "blog/multichannel-led-driver/index.rst">}} -.. _`LED characterization`: {{<ref "blog/led-characterization/index.rst">}} -.. _ESP8266: https://en.wikipedia.org/wiki/ESP8266 -.. _RS485: https://en.wikipedia.org/wiki/RS-485 -.. _Phoenix-style: https://www.phoenixcontact.com/online/portal/de?uri=pxc-oc-itemdetail:pid=1757019&library=dede&tab=1 -.. _STM32: http://www.st.com/resource/en/datasheet/stm32f030f4.pdf -.. _ESP-01: http://www.watterott.com/de/ESP8266-WiFi-Serial-Transceiver-Modul -.. _74HC595: http://www.ti.com/lit/ds/symlink/sn74hc595.pdf -.. _AO3400: http://aosmd.com/pdfs/datasheet/AO3400.pdf -.. _MOSFETs: https://en.wikipedia.org/wiki/MOSFET -.. _EMI: https://en.wikipedia.org/wiki/Electromagnetic_interference -.. _polyfuse: https://en.wikipedia.org/wiki/Resettable_fuse -.. _SMD: https://en.wikipedia.org/wiki/Surface-mount_technology -.. _fuses: https://en.wikipedia.org/wiki/Fuse_(electrical) -.. _littlefuse-16r-datasheet: http://m.littelfuse.com/~/media/electronics/datasheets/resettable_ptcs/littelfuse_ptc_16r_datasheet.pdf.pdf -.. _OpenSCAD: http://www.openscad.org/ -.. _STL: https://en.wikipedia.org/wiki/STL_(file_format) -.. _PNG: https://en.wikipedia.org/wiki/Portable_Network_Graphics -.. _esphttpd: https://github.com/Spritetm/esphttpd -.. _Spritetm: http://spritesmods.com/ -.. _`HSV`: https://en.wikipedia.org/wiki/HSL_and_HSV - diff --git a/content/imprint/index.rst b/content/imprint/index.rst deleted file mode 100644 index 12901ea..0000000 --- a/content/imprint/index.rst +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: "Impressum" ---- - -Impressum ---------- - -.. raw:: html - - <p> - Sebastian Götte<br/> - c/o Praxis Dr. Götte<br/> - Krankenhausstr. 1a<br/> - 54634 Bitburg<br/> - imprint@jaseg.net - </p> - - Inhaltlich Verantwortlicher gem. § 55 II RStV: Sebastian Götte (Anschrift s.o.) - -Lizenz dieser Seite -------------------- - -.. raw:: html - - Dieses Werk ist lizenziert unter einer - <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"> - Creative Commons Namensnennung - Nicht-kommerziell - Weitergabe unter gleichen Bedingungen 4.0 International - Lizenz (CC-BY-NC-SA) - </a>.<br/> - <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"> - <img alt="Creative Commons Lizenzvertrag" style="border-width:0" - src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /> - </a> - - -Haftungsbeschränkung für Inhalte der Website --------------------------------------------- - -Gemäß § 7 Abs. 1 TMG ist der Verantwortliche der Website i. S. v. § 5 TMG für eigene Informationen, die er zur Nutzung -bereithält, nach den allgemeinen Gesetzen verantwortlich. - -Der Verantwortliche der vorbezeichneten Website übernimmt keine Haftung auf Aktualität, Richtigkeit und Vollständigkeit -der auf dieser Website zur Verfügung gestellten Inhalte. Dies gilt nicht, wenn dem Verantwortlichen vorsätzliches oder -grob fahrlässiges Verhalten vorzuwerfen ist. Die Inhalte wurden mit der größtmöglichen Sorgfalt erstellt. Dennoch kann -die inhaltliche Richtigkeit insbesondere bei komplexen Themen nicht gewährleistet werden, so dass der Verantwortliche -den Nutzern empfiehlt, bei wichtigen Informationen bei den zuständigen Stellen anzufragen oder rechtliche Beratung in -Anspruch zu nehmen. Sofern kostenpflichtige Inhalte oder Dienste auf der Website zur Verfügung gestellt werden, handelt -es sich dabei um unverbindliche Invitatio ad offerendum, welche lediglich zur Abgabe eines Angebots durch den Nutzer -aufrufen und selbst kein verbindliches Angebot darstellen. - -Gemäß §§ 8 ff. TMG ist der Website-Betreiber für fremde Inhalte, die er für einen Nutzer veröffentlicht, nicht -verantwortlich, sofern - -* er keine Kenntnis von der rechtswidrigen Handlung oder der Information hat und ihm im Falle von - Schadensersatzansprüchen auch keine Tatsachen oder Umstände bekannt sind, aus denen die rechtswidrige Handlung oder - die Information offensichtlich wird, oder - -* er unverzüglich tätig geworden ist, um die Information zu entfernen oder den Zugang zu ihr zu sperren, sobald er - diese Kenntnis erlangt hat. - -Erlangt der Website-Betreiber Kenntnis von solchen rechtswidrigen Inhalten, werden diese unverzüglich entfernt. - -Haftung für ausgehende Links ----------------------------- - -Der Website-Betreiber verlinkt von seiner Website auf fremde Websites. Durch diese sog. „Hyperlinks“ wird der Nutzer -direkt auf die fremde Website geleitet. Der Website-Betreiber hat dabei keinerlei Einfluss auf die Informationen der -fremden Website. Daher kann keine Haftung für die Aktualität, Richtigkeit und Vollständigkeit der Inhalte der fremden -Website übernommen werden. Der Website-Betreiber versichert jedoch, dass ihm zum Zeitpunkt des Setzens der Verlinkung -keinerlei rechtliche Verstöße bekannt waren und er die fremde Website im Rahmen des Zumutbaren geprüft hat. - -Erhält der Website-Betreiber Kenntnis von der Rechtswidrigkeit der verlinkten Inhalte, wird der entsprechende Link -unverzüglich entfernt. - -Urheberrechte -------------- - -Die auf dieser Webseite veröffentlichten Inhalte unterliegen dem deutschen Urheberrecht. - -Als Urheber i. S. v. § 7 UrhG stehen dem Website-Betreiber die alleinigen und ausschließlichen Verwertungsrechte gemäß -§§ 15 ff. UrhG zu. Eine Vervielfältigung oder Verwendung sämtlicher Inhalte der Website in fremden elektronischen oder -gedruckten Medien ist ohne vorherige Zustimmung des Website-Betreibers untersagt und wird ggf. verfolgt. - -Datenschutz ------------ - -Die Website kann grundsätzlich ohne Eingabe von personenbezogenen Daten wie z. B. Name oder E-Mail-Adresse genutzt -werden. Sollte die Möglichkeit der Eingabe solcher Daten bestehen, so erfolgt die Mitteilung dieser Daten auf -freiwilliger Basis durch den Nutzer. Eine Weitergabe dieser Daten an Dritte ist ohne die ausdrückliche Zustimmung durch -den Nutzer ausgeschlossen. - -Ein lückenloser Schutz der übermittelten Daten vor dem Zugriff durch unbefugte Dritte ist jedoch nicht möglich, da es im -Bereich der Datenübermittlung im Internet zu Sicherheitslücken kommen kann. Der Website-Betreiber versucht jedoch, die -Gefahr des unbefugten Zugriffs durch geeignete Maßnahmen zu unterbinden und im Falle der Kenntnis von einer -Sicherheitslücke diese durch geeignete Maßnahmen zu schließen. - -Der Nutzung der im Impressum veröffentlichten Kontaktdaten durch Dritte zur Übersendung von nicht ausdrücklich -gewünschter Werbung, insbesondere Spam-Mails, wird widersprochen und im Falle der Nichtbeachtung ggf. mit rechtlichen -Schritten verfolgt. - -(Quelle: `BUSE HERZ GRUNST Rechtsanwälte <https://www.kanzlei-wirtschaftsrecht.berlin/aktuelles/kostenloser-muster-disclaimer>`__) - diff --git a/content/projects/_index.rst b/content/projects/_index.rst deleted file mode 100644 index 9f9d49b..0000000 --- a/content/projects/_index.rst +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: Projects -hide_date: true ---- -I maintain a number of open-source projects. Most of these I started out of some personal need or interest. -I strive to keep all of them up to date and maintained, so if you notice an issue with one of them, please -open an issue on the project's issue tracker. diff --git a/content/projects/gerbolyze/README.rst b/content/projects/gerbolyze/README.rst deleted file mode 100644 index 0e8262b..0000000 --- a/content/projects/gerbolyze/README.rst +++ /dev/null @@ -1,700 +0,0 @@ -Gerbolyze renders SVG vector and PNG/JPG raster images into existing gerber PCB manufacturing files. -Vector data from SVG files is rendered losslessly *without* an intermediate rasterization/revectorization step. -Still, gerbolyze supports (almost) the full SVG 1.1 spec including complex, self-intersecting paths with holes, -patterns, dashes and transformations. - -Raster images can either be vectorized through contour tracing (like gerbolyze v1.0 did) or they can be embedded using -high-resolution grayscale emulation while (mostly) guaranteeing trace/space design rules. - -Try gerbolyze online at https://dyna.kokoroyukuma.de/gerboweb - -.. figure:: pics/pcbway_sample_02_small.jpg - :width: 800px - - Drawing by `トーコ Toko <https://twitter.com/fluffy2038/status/1317231121269104640>`__ converted using Gerbolyze and printed at PCBWay. - - -Tooling for PCB art is quite limited in both open source and closed source ecosystems. Something as simple as putting a -pretty picture on a PCB can be an extremely tedious task. Depending on the PCB tool used, various arcane incantations -may be necessary and even modestly complex images will slow down most PCB tools to a crawl. - -Gerbolyze solves this problem in a toolchain-agnostic way by directly vectorizing SVG vector and PNG or JPG bitmap files -onto existing gerber layers. Gerbolyze processes any spec-compliant SVG and "gerbolyzes" SVG vector data into a Gerber -spec-compliant form. Gerbolyze has been tested against both the leading open-source KiCAD toolchain and the -industry-standard Altium Designer. Gerbolyze is written with performance in mind and will happily vectorize tens of -thousands of primitives, generating tens of megabytes of gerber code without crapping itself. With gerbolyze you can -finally be confident that your PCB fab's toolchain will fall over before yours does if you overdo it with the high-poly -anime silkscreen. - -Gerbolyze is based on gerbonara_. - -.. image:: pics/process-overview.png - :width: 800px - -.. contents:: - -Tl;dr: Produce high-quality artistic PCBs in three easy steps! --------------------------------------------------------------- - -Gerbolyze works in three steps. - -1. Generate a scale-accurate template of the finished PCB from your CAD tool's gerber output: - - .. code:: - - $ gerbolyze template --top template_top.svg [--bottom template_bottom.svg] my_gerber_dir - -2. Load the resulting template image Inkscape_ or another SVG editing program. Put your artwork on the appropriate SVG - layer. Dark colors become filled gerber primitives, bright colors become unfilled primitives. You can directly put - raster images (PNG/JPG) into this SVG as well, just position and scale them like everything else. SVG clips work for - images, too. Masks are not supported. - -3. Vectorize the edited SVG template image drectly into the PCB's gerber files: - - .. code:: - - $ gerbolyze paste --top template_top_edited.svg [--bottom ...] my_gerber_dir output_gerber_dir - -Quick Start Installation (Any Platform) ---------------------------------------- - -.. code-block:: shell - - python -m pip install --user gerbolyze - -To uninstall, run - -.. code-block:: shell - - python -m pip uninstall gerbolyze gerbonara resvg-wasi svg-flatten-wasi - -To update, run - -.. code-block:: shell - - python -m pip install --user --upgrade --upgrade-strategy eager gerbolyze - -Speeding up gerbolyze using natively-built binaries ---------------------------------------------------- - -This will install gerbolyze's binary dependency resvg and gerbolyze's svg-flatten utility as pre-built cross-platform -WASM binaries. When you first run gerbolyze, it will take some time (~30s) to link these binaries for your system. The -output is cached, so any future run is going to be fast. - -WASM is slower than natively-built binaries. To speed up gerbolyze, you can natively build its two binary dependencies: - -1. Install resvg natively using rust's cargo package manager: ``cargo install resvg`` -2. Install gerbolyze's svg-flatten utility natively. You can get pre-built binaries from gerbolyze's gitlab CI jobs `at - this link <https://gitlab.com/gerbolyze/gerbolyze/-/pipelines?scope=tags&page=1>`__ by clicking the three dots on the - right next to the version you want. These pre-built binaries should work on any x86_64 linux since they are - statically linked. You can also build svg-flatten yourself by running ``make`` inside the ``svg-flatten`` folder from - a gerbolyze checkout. - -Gerbolyze will pick up these binaries when installed in your ``$PATH``. resvg is also picked up when it is installed by -cargo in your home's ``~/.cargo``, even if it's not in your ``$PATH``. You can override the resvg, usvg or svg-flatten -binary that gerbolyze uses by giving it the absoulute path to a binary in the ``$RESVG``, ``$USVG`` and ``$SVG_FLATTEN`` -environment variables. - - -Build from source (any distro) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: sh - - git clone --recurse-submodules https://git.jaseg.de/gerbolyze.git - cd gerbolyze - - python3 -m venv - source venv/bin/activate - python3 setup.py install - -Features --------- - -Input on the left, output on the right. - -.. image:: pics/test_svg_readme_composited.png - :width: 800px - -* Almost full SVG 1.1 static spec coverage (!) - - * Paths with beziers, self-intersections and holes - * Strokes, even with dashes and markers - * Pattern fills and strokes - * Transformations and nested groups - * Proper text rendering with support for complex text layout (e.g. Arabic) - * <image> elements via either built-in vectorizer or built-in halftone processor - * (some) CSS - -* Writes Gerber, SVG or KiCAD S-Expression (``.kicad_mod``) formats -* Can export from top/bottom SVGs to a whole gerber layer stack at once with filename autodetection -* Can export SVGs to ``.kicad_mod`` files like svg2mod (but with full SVG support) -* Beziers flattening with configurable tolerance using actual math! -* Polygon intersection removal -* Polygon hole removal (!) -* Optionally vector-compositing of output: convert black/white/transparent image to black/transparent image -* Renders SVG templates from input gerbers for accurate and easy scaling and positioning of artwork -* layer masking with offset (e.g. all silk within 1mm of soldermask) -* Can read gerbers from zip files -* Limited SVG support for board outline layers (no fill/region support) -* Dashed lines supported on board outline layers - -Gerbolyze is the end-to-end "paste this svg into these gerbers" command that handles all layers on both board sides at -once. The heavy-duty computer geometry logic of gerbolyze is handled by the svg-flatten utility (``svg-flatten`` -directory). svg-flatten reads an SVG file and renders it into a variety of output formats. svg-flatten can be used like -a variant of the popular svg2mod that supports all of SVG and handles arbitrary input ``<path>`` elements. - -Algorithm Overview ------------------- - -This is the algorithm gerbolyze uses to process a stack of gerbers. - -* Map input files to semantic layers by their filenames -* For each layer: - - * load input gerber - * Pass mask layers through ``gerbv`` for conversion to SVG - * Pass mask layers SVG through ``svg-flatten --dilate`` - * Pass input SVG through ``svg-flatten --only-groups [layer]`` - * Overlay input gerber, mask and input svg - * Write result to output gerber - -This is the algorithm svg-flatten uses to process an SVG. - -* pass input SVG through usvg_ -* iterate depth-first through resulting SVG. - - * for groups: apply transforms and clip and recurse - * for images: Vectorize using selected vectorizer - * for paths: - - * flatten path using Cairo - * remove self-intersections using Clipper - * if stroke is set: process dash, then offset using Clipper - * apply pattern fills - * clip to clip-path - * remove holes using Clipper - -* for KiCAD S-Expression export: vector-composite results using CavalierContours: subtract each clear output primitive - from all previous dark output primitives - -Web interface -------------- - -You can try gerbolyze online at https://dyna.kokoroyukuma.de/gerboweb - -The web interface does not expose all of gerbolyze's bells and whistles, but it allows you to simply paste a single SVG -file on a board to try out gerbolyze. Upload your design on the web interface, then download the template for either the -top or bottom side, and put your artwork on the appropriate layer of that template using Inkscape_. Finally, upload the -modified template and let gerbolyze process your design. - -Command-line usage ------------------- -.. _command_line_usage: - -Generate SVG template from Gerber files: - -.. code-block:: shell - - gerbolyze template [options] [--top|--bottom] input_dir_or.zip output.svg - -Render design from an SVG made with the template above into a set of gerber files: - -.. code-block:: shell - - gerbolyze paste [options] artwork.svg input_dir_or.zip output_dir_or.zip - -Use svg-flatten to convert an SVG file into Gerber or flattened SVG: - -.. code-block:: shell - - svg-flatten [options] --format [gerber|svg] [input_file.svg] [output_file] - -Use svg-flatten to convert an SVG file into the given layer of a KiCAD S-Expression (``.kicad_mod``) file: - -.. code-block:: shell - - svg-flatten [options] --format kicad --sexp-layer F.SilkS --sexp-mod-name My_Module [input_file.svg] [output_file] - -Use svg-flatten to convert an SVG file into a ``.kicad_mod`` with SVG layers fed into separate KiCAD layers based on -their IDs like the popular ``svg2mod`` is doing: - -Note: - Right now, the input SVG's layers must have *ids* that match up KiCAD's s-exp layer names. Note that when you name - a layer in Inkscape that only sets a ``name`` attribute, but does not change the ID. In order to change the ID in - Inkscape, you have to use Inkscape's "object properties" context menu function. - - Also note that svg-flatten expects the layer names KiCAD uses in their S-Expression format. These are *different* to - the layer names KiCAD exposes in the UI (even though most of them match up!). - - For your convenience, there is an SVG template with all the right layer names and IDs located next to this README. - -.. code-block:: shell - - svg-flatten [options] --format kicad --sexp-mod-name My_Module [input_file.svg] [output_file] - -``gerbolyze template`` -~~~~~~~~~~~~~~~~~~~~~~ - -Usage: ``gerbolyze template [OPTIONS] INPUT`` - -Generate SVG template for gerbolyze paste from gerber files. - -INPUT may be a gerber file, directory of gerber files or zip file with gerber files. The output file contains a preview -image of the input gerbers to allow you to position your artwork, as well as prepared Inkscape layers corresponding to -each gerber layer. Simply place your artwork in this SVG template using Inkscape. Starting in v3.0, gerbolyze -automatically keeps track of which board side (top or bottom) is contained in an SVG template. - -Options: -******** -``--top | --bottom`` - Output top or bottom side template. This affects both the preview image and the prepared Inkscape layers. - -``--vector | --raster`` - Embed preview renders into output file as SVG vector graphics instead of rendering them to PNG bitmaps. The - resulting preview may slow down your SVG editor. - -``--raster-dpi FLOAT`` - DPI for rastering preview - -``--bbox TEXT`` - Output file bounding box. Format: "w,h" to force [w] mm by [h] mm output canvas OR "x,y,w,h" to force [w] mm by [h] - mm output canvas with its bottom left corner at the given input gerber coördinates. - - -``gerbolyze paste`` -~~~~~~~~~~~~~~~~~~~ -(see `below <vectorization_>`__) - -Usage: ``gerbolyze paste [OPTIONS] INPUT_GERBERS OVERLAY_SVG OUTPUT_GERBERS`` - -Render vector data and raster images from SVG file into gerbers. The SVG input file can be generated using ``gerbolyze -template`` and contains the name and board side of each layer. Note that for board outline layers, handling slightly -differs from other layers as PCB fabs do not support filled Gerber regions on these layers. - -Options: -******** - -``--bbox TEXT`` - Output file bounding box. Format: "w,h" to force [w] mm by [h] mm output canvas OR "x,y,w,h" to force [w] mm by [h] - mm output canvas with its bottom left corner at the given input gerber coördinates. This **must match the ``--bbox`` value given to - template**! - -``--subtract TEXT`` - Use user subtraction script from argument (see `below <subtraction_script_>`_) - -``--no-subtract`` - Disable subtraction (see `below <subtraction_script_>`_) - -``--dilate FLOAT`` - Default dilation for subtraction operations in mm (see `below <subtraction_script_>`_) - -``--trace-space FLOAT`` - Passed through to svg-flatten, see `below <svg_flatten_>`__. - -``--vectorizer TEXT`` - Passed through to svg-flatten, see `its description below <svg_flatten_>`__. Also have a look at `the examples below <vectorization_>`_. - -``--vectorizer-map TEXT`` - Passed through to svg-flatten, see `below <svg_flatten_>`__. - -``--exclude-groups TEXT`` - Passed through to svg-flatten, see `below <svg_flatten_>`__. - - -.. _outline_layers: - -Outline layers -************** - -Outline layers require special handling since PCB fabs do not support filled G36/G37 polygons on these layers. The main -difference between normal layers and outline layers is how strokes are handled. On outline layers, strokes are -translated to normal Gerber draw commands (D01, D02 etc.) with an aperture set to the stroke's width instead of tracing -them to G36/G37 filled regions. This means that on outline layers, SVG end caps and line join types do not work: All -lines are redered with round joins and end caps. - -One exception from this are patterns, which work as expected for both fills and strokes with full support for joins and -end caps. - -Dashed strokes are supported on outline layers and can be used to make easy mouse bites. - -.. _subtraction_script: - -Subtraction scripts -******************* - -.. image:: pics/subtract_example.png - :width: 800px - -Subtraction scripts tell ``gerbolyze paste`` to remove an area around certain input layers to from an overlay layer. -When a input layer is given in the subtraction script, gerbolyze will dilate (extend outwards) everything on this input -layer and remove it from the target overlay layer. By default, Gerbolyze subtracts the mask layer from the silk layer to -make sure there are no silk primitives that overlap bare copper, and subtracts each input layer from its corresponding -overlay to make sure the two do not overlap. In the picture above you can see both at work: The overlay contains -halftone primitives all over the place. The subtraction script has cut out an area around all pads (mask layer) and all -existing silkscreen. You can turn off this behavior by passing ``--no-subtract`` or pass your own "script". - -The syntax of these scripts is: - -.. code-block:: - - {target layer} -= {source layer} {dilation} [; ...] - -The target layer must be ``out.{layer name}`` and the source layer ``in.{layer name}``. The layer names are gerbolyze's -internal layer names, i.e.: ``paste, silk, mask, copper, outline, drill`` - -The dilation value is optional, but can be a float with a leading ``+`` or ``-``. If given, before subtraction the -source layer's features will be extended by that many mm. If not given, the dilation defaults to the value given by -``--dilate`` if given or 0.1 mm otherwise. To disable dilation, simply pass ``+0`` here. - -Multiple commands can be separated by semicolons ``;`` or line breaks. - -The default subtraction script is: - -.. code-block:: - - out.silk -= in.mask - out.silk -= in.silk+0.5 - out.mask -= in.mask+0.5 - out.copper -= in.copper+0.5 - -.. _svg_flatten: - -``svg-flatten`` -~~~~~~~~~~~~~~~ - -Usage: ``svg-flatten [OPTIONS]... [INPUT_FILE] [OUTPUT_FILE]`` - -Specify ``-`` for stdin/stdout. - -Options: -******** - -``-h, --help`` - Print help and exit - -``-v, --version`` - Print version and exit - -``-o, --format`` - Output format. Supported: gerber, gerber-outline (for board outline layers), svg, s-exp (KiCAD S-Expression) - -``-p, --precision`` - Number of decimal places use for exported coordinates (gerber: 1-9, SVG: >=0). Note that not all gerber viewers are - happy with too many digits. 5 or 6 is a reasonable choice. - -``--clear-color`` - SVG color to use in SVG output for "clear" areas (default: white) - -``--dark-color`` - SVG color to use in SVG output for "dark" areas (default: black) - -``-f, --flip-gerber-polarity`` - Flip polarity of all output gerber primitives for --format gerber. - -``-d, --trace-space`` - Minimum feature size of elements in vectorized graphics (trace/space) in mm. Default: 0.1mm. - -``--no-header`` - Do not export output format header/footer, only export the primitives themselves - -``--flatten`` - Flatten output so it only consists of non-overlapping white polygons. This perform composition at the vector level. - Potentially slow. This defaults to on when using KiCAD S-Exp export because KiCAD does not know polarity or colors. - -``--no-flatten`` - Disable automatic flattening for KiCAD S-Exp export - -``--dilate`` - Dilate output gerber primitives by this amount in mm. Used for masking out other layers. - -``-g, --only-groups`` - Comma-separated list of group IDs to export. - -``-b, --vectorizer`` - Vectorizer to use for bitmap images. One of poisson-disc (default), hex-grid, square-grid, binary-contours, - dev-null. Have a look at `the examples below <vectorization_>`_. - -``--vectorizer-map`` - Map from image element id to vectorizer. Overrides --vectorizer. Format: id1=vectorizer,id2=vectorizer,... - - You can use this to set a certain vectorizer for specific images, e.g. if you want to use both halftone - vectorization and contour tracing in the same SVG. Note that you can set an ``<image>`` element's SVG ID from within - Inkscape though the context menu's Object Properties tool. - -``--force-svg`` - Force SVG input irrespective of file name - -``--force-png`` - Force bitmap graphics input irrespective of file name - -``-s, --size`` - Bitmap mode only: Physical size of output image in mm. Format: 12.34x56.78 - -``--sexp-mod-name`` - Module name for KiCAD S-Exp output. This is a mandatory argument if using S-Exp output. - -``--sexp-layer`` - Layer for KiCAD S-Exp output. Defaults to auto-detect layers from SVG layer/top-level group IDs. If given, SVG - groups and layers are completely ignored and everything is simply vectorized into this layer, though you cna still - use ``-g`` for group selection. - -``-a, --preserve-aspect-ratio`` - Bitmap mode only: Preserve aspect ratio of image. Allowed values are meet, slice. Can also parse full SVG - preserveAspectRatio syntax. - -``--no-usvg`` - Do not preprocess input using usvg (do not use unless you know *exactly* what you're doing) - -``--usvg-dpi`` - Passed through to usvg's --dpi, in case the input file has different ideas of DPI than usvg has. - -``--scale`` - Scale input svg lengths by this factor (-o gerber only). - -``-e, --exclude-groups`` - Comma-separated list of group IDs to exclude from export. Takes precedence over --only-groups. - -.. _vectorization: - -Gerbolyze image vectorization ------------------------------ - -Gerbolyze has two built-in strategies to translate pixel images into vector images. One is its built-in halftone -processor that tries to approximate grayscale. The other is its built-in binary vectorizer that traces contours in -black-and-white images. Below are examples for the four options. - -The vectorizers can be used in isolation through ``svg-flatten`` with either an SVG input that contains an image or a -PNG/JPG input. - -The vectorizer can be controlled globally using the ``--vectorizer`` flag in both ``gerbolyze`` and ``svg-flatten``. It -can also be set on a per-image basis in both using ``--vectorizer-map [image svg id]=[option]["," ...]``. - -.. for f in vec_*.png; convert -background white -gravity center $f -resize 500x500 -extent 500x500 (basename -s .png $f)-square.png; end -.. for vec in hexgrid square poisson contours; convert vec_"$vec"_whole-square.png vec_"$vec"_detail-square.png -background transparent -splice 25x0+0+0 +append -chop 25x0+0+0 vec_"$vec"_composited.png; end - -``--vectorizer poisson-disc`` (the default) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. image:: pics/vec_poisson_composited.png - :width: 800px - -``--vectorizer hex-grid`` -~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. image:: pics/vec_hexgrid_composited.png - :width: 800px - -``--vectorizer square-grid`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. image:: pics/vec_square_composited.png - :width: 800px - -``--vectorizer binary-contours`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. image:: pics/vec_contours_composited.png - :width: 800px - -The binary contours vectorizer requires a black-and-white binary input image. As you can see, like every bitmap tracer -it will produce some artifacts. For artistic input this is usually not too bad as long as the input data is -high-resolution. Antialiased edges in the input image are not only OK, they may even help with an accurate -vectorization. - -GIMP halftone preprocessing guide ---------------------------------- - -Gerbolyze has its own built-in halftone processor, but you can also use the high-quality "newsprint" filter built into -GIMP_ instead if you like. This section will guide you through this. The PNG you get out of this can then be fed into -gerbolyze using ``--vectorizer binary-contours``. - -1 Import your desired artwork -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Though anime or manga pictures are highly recommended, you can use any image including photographs. Be careful to select -a picture with comparatively low detail that remains recognizable at very low resolution. While working on a screen this -is hard to vizualize, but the grain resulting from the low resolution of a PCB's silkscreen is quite coarse. - -.. image:: screenshots/02import02.png - :width: 800px - -2 Convert the image to grayscale -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. image:: screenshots/06grayscale.png - :width: 800px - -3 Fine-tune the image's contrast -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To look well on the PCB, contrast is critical. If your source image is in color, you may have lost some contrast during -grayscale conversion. Now is the time to retouch that using the GIMP's color curve tool. - -When using the GIMP's newsprint filter, bright grays close to white and dark grays close to black will cause very small -dots that might be beyond your PCB manufacturer's maximum resolution. To control this case, add small steps at the ends -of the grayscale value curve as shown (exaggerated) in the picture below. These steps saturate very bright grays to -white and very dark grays to black while preserving the values in the middle. - -.. image:: screenshots/08curve_cut.png - :width: 800px - -4 Retouch details -~~~~~~~~~~~~~~~~~ - -Therer might be small details that don't look right yet, such as the image's background color or small highlights that -merge into the background now. You can manually change the color of any detail now using the GIMP's flood-fill tool. - -If you don't want the image's background to show up on the final PCB at all, just make it black. - -Particularly on low-resolution source images it may make sense to apply a blur with a radius similar to the following -newsprint filter's cell size (10px) to smooth out the dot pattern generated by the newsprint filter. - -.. image:: screenshots/09retouch.png - :width: 800px - -In the following example, I retouched the highlights in the hair of the character in the picture to make them completely -white instead of light-gray, so they still stand out nicely in the finished picture. - -.. image:: screenshots/10retouched.png - :width: 800px - -5 Run the newsprint filter -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Now, run the GIMP's newsprint filter, under filters, distorts, newsprint. - -The first important settings is the spot size, which should be larger than your PCB's minimum detail size (about 10px -with ``gerbolyze render`` default settings for good-quality silkscreen). In general the cheap and fast standard option of chinese PCB houses will require a larger detail size, but when you order specialty options like large size, 4-layer or non-green color along with a longer turnaround time you'll get much better-quality silk screen. - -The second important setting is oversampling, which should be set to four or slightly higher. This improves the result -of the edge reconstruction of ``gerbolyze vectorize``. - -.. image:: screenshots/11newsprint.png - :width: 800px - -The following are examples on the detail resulting from the newsprint filter. - -.. image:: screenshots/12newsprint.png - :width: 800px - -6 Export the image for use with ``gerbolyze vectorize`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Simply export the image as a PNG file. Below are some pictures of the output ``gerbolyze vectorize`` produced for this -example. - -.. image:: screenshots/14result_cut.png - :width: 800px - -.. image:: screenshots/15result_cut.png - :width: 800px - -Manufacturing Considerations ----------------------------- - -The main consideration when designing artwork for PCB processes is the processes' trace/space design rule. The two -things you can do here is one, to be creative with graphical parts of the design and avoid extremely narrow lines, -wedges or other thin features that will not come out well. Number two is to keep detail in raster images several times -larger than the manufacturing processes native capability. For example, to target a trace/space design rule of 100 µm, -the smallest detail in embedded raster graphics should not be much below 1mm. - -Gerbolyze's halftone vectorizers have built-in support for trace/space design rules. While they can still produce small -artifacts that violate these rules, their output should be close enough to satifsy board houses and close enough for the -result to look good. The way gerbolyze does this is to clip the halftone cell's values to zero whenevery they get too -small, and to forcefully split or merge two neighboring cells when they get too close. While this process introduces -slight steps at the top and bottom of grayscale response, for most inputs these are not noticeable. - -On the other hand, for SVG vector elements as well as for traced raster images, Gerbolyze cannot help with these design -rules. There is no heuristic that would allow Gerbolyze to non-destructively "fix" a design here, so all that's on the -roadmap here is to eventually include a gerber-level design rule checker. - -As far as board houses go, I have made good experiences with the popular Chinese board houses. In my experience, JLC -will just produce whatever you send them with little fucks being given about design rule adherence or validity of the -input gerbers. This is great if you just want artistic circuit boards without much of a hassle, and you don't care if -they come out exactly as you imagined. The worst I've had happen was when an older version of gerbolyze generated -polygons with holes assuming standard fill-rule processing. The in the board house's online gerber viewer things looked -fine, and neither did they complain during file review. However, the resulting boards looked completely wrong because -all the dark halftones were missing. - -PCBWay on the other hand has a much more rigurous file review process. They <em>will</em> complain when you throw -illegal garbage gerbers at them, and they will helpfully guide you through your design rule violations. In this way you -get much more of a professional service from them and for designs that have to be functional their higher level of -scrutiny definitely is a good thing. For the design you saw in the first picture in this article, I ended up begging -them to just plot my files if it doesn't physically break their machines and to their credit, while they seemed unhappy -about it they did it and the result looks absolutely stunning. - -PCBWay is a bit more expensive on their lowest-end offering than JLC, but I found that for anything else (large boards, -multi-layer, gold plating etc.) their prices match. PCBWay offers a much broader range of manufacturing options such as -flexible circuit boards, multi-layer boards, thick or thin substrates and high-temperature substrates. - -When in doubt about how your design is going to come out on the board, do not hesitate to contact your board house. Most -of the end customer-facing online PCB services have a number of different factories that do a number of different -fabrication processes for them depending on order parameters. Places like PCBWay have exceptional quality control and -good customer service, but that is mostly focused on the technical aspects of the PCB. If you rely on visual aspects -like silkscreen uniformity or solder mask color that is a strong no concern to everyone else in the electronics -industry, you may find significant variations between manufacturers or even between orders with the same manufacturer -and you may encounter challenges communicating your requirements. - -Limitations ------------ - -SVG raster features -~~~~~~~~~~~~~~~~~~~ - -Currently, SVG masks and filters are not supported. Though SVG is marketed as a "vector graphics format", these two -features are really raster primitives that all SVG viewers perform at the pixel level after rasterization. Since -supporting these would likely not end up looking like what you want, it is not a planned feature. If you need masks or -filters, simply export the relevant parts of the SVG as a PNG then include that in your template. - -Gerber pass-through -~~~~~~~~~~~~~~~~~~~ - -Since gerbolyze has to composite your input gerbers with its own output, it has to fully parse and re-serialize them. -gerbolyze gerbonara_ for all its gerber parsing needs. Thus, gerbonara will interpret your gerbers and output will be in -gerbonara's gerber "dialect". If you find a corner case where this does not work and the output looks wrong, please file -a bug report with an example file on the gerbonara_ bug tracker. *Always* check the output files for errors before -submitting them to production. - -Gerbolyze is provided without any warranty, but still please open an issue or `send me an email -<mailto:gerbolyze@jaseg.de>`__ if you find any errors or inconsistencies. - -Trace/Space design rule adherence -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -While the grayscale halftone vectorizers do a reasonable job adhering to a given trace/space design rule, they can still -produce small parts of output that violate it. For the contour vectorizer as well as for all SVG primitives, you are -responsible for adhering to design rules yourself as there is no algorithm that gerboyze could use to "fix" its input. - -A design rule checker is planned as a future addition to gerbolyze, but is not yet part of it. If in doubt, talk to your -fab and consider doing a test run of your design before ordering assembled boards ;) - -Gallery -------- - -.. image:: pics/sample3.jpg - :width: 400px - -For a demonstration of ``gerbolyze convert``, check out the `Gerbolyze Protoboard Index`_, where you can download gerber -files for over 7.000 SMD and THT protoboard layouts. - -Licensing ---------- - -This tool is licensed under the rather radical AGPLv3 license. Briefly, this means that you have to provide users of a -webapp using this tool in the backend with this tool's source. - -I get that some people have issues with the AGPL. In case this license prevents you from using this software, please -send me `an email <mailto:agpl.sucks@jaseg.de>`__ and I can grant you an exception. I want this software to be useful to as -many people as possible and I wouldn't want the license to be a hurdle to anyone. OTOH I see a danger of some cheap -board house just integrating a fork into their webpage without providing their changes back upstream, and I want to -avoid that so the default license is still AGPL. - -.. _usvg: https://github.com/RazrFalcon/resvg -.. _Inkscape: https://inkscape.org/ -.. _pcb-tools: https://github.com/curtacircuitos/pcb-tools -.. _pcb-tools-extension: https://github.com/opiopan/pcb-tools-extension -.. _GIMP: https://gimp.org/ -.. _gerbonara: https://gitlab.com/gerbolyze/gerbonara -.. _`Gerbolyze Protoboard Index`: https://dyna.kokoroyukuma.de/protos/ - diff --git a/content/projects/gerbolyze/index.rst b/content/projects/gerbolyze/index.rst deleted file mode 100644 index 0f0ed47..0000000 --- a/content/projects/gerbolyze/index.rst +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: "Gerbolyze" -external_links: - - name: Sources - url: "https://git.jaseg.de/gerbolyze.git" - - name: Issues - url: "https://github.com/jaseg/gerbolyze/issues" - - name: Docs - url: "https://gerbolyze.gitlab.io/gerbolyze" - - name: PyPI - url: "https://pypi.org/project/gerbolyze" -summary: > - Gerbolyze is a tool that allows the modification of Gerber PCB artwork with a vector graphics editor like Inkscape. - Gerbolyze directly converts between SVG and Gerber, and accurately reproduces details that other tools can not. ---- - -.. include:: content/projects/gerbolyze/README.rst diff --git a/content/projects/gerbonara/index.rst b/content/projects/gerbonara/index.rst deleted file mode 100644 index 6069351..0000000 --- a/content/projects/gerbonara/index.rst +++ /dev/null @@ -1,141 +0,0 @@ ---- -title: "Gerbonara" -external_links: - - name: Sources - url: "https://git.jaseg.de/gerbonara.git" - - name: Issues - url: "https://gitlab.com/gerbolyze/gerbonara/issues" - - name: Docs - url: "https://gerbolyze.gitlab.io/gerbonara" - - name: PyPI - url: "https://pypi.org/project/gerbonara" -summary: > - Gerbonara is a user-friendly, powerful tool for reading, writing, modification and rendering of Gerber PCB artwork - from the command line or from Python code. Gerbonara supports the Gerber dialects of all industry-standard EDA - tools. ---- - -Gerbonara is a library to read, modify and write PCB manufacturing files such as Gerber, Excellon and IPC-356 through a -pythonic API. Gerbonara can open a folder of manufacturing files, and parse file names and metadata to figure out which -file contains what. Gerbonara is tested using an extensive library of real-world example files from CAD tools including -KiCAD, Altium, Eagle, Allegro, gEDA, Fritzing, Siemens/Mentor Graphics PADS, and Target3001!. - -Gerbonara's API is built on two principles: - -**Meaningful, object-oriented API** - Gerbonara abstracts away the details of the underlying file format such as tool indices, coordinate notation and - graphical state, and presents meaningful "graphical objects" such as a `primitives.Line`, - `primitives.Arc`, or `Region` through its API. These objects can be easily created, - manipulated or deleted from code without breaking anything else. You can even copy graphical objects between files, - and Gerbonara will automatically convert coordinate format, units etc. for you. `GerberFile` and - `ExcellonFile` use the same types of `graphic objects <object-api>`, so objects can be directly - copied between file types without conversion. - -**Unit-safety** - Gerbonara embeds physical `LengthUnit` information in all objects. The high-level API such as - `LayerStack.merge` or `GerberFile.offset` accepts arguments with an explicitly given unit and - automatically converts them as needed. Objects can be copied between `GerberFile` instances and unit - conversion will be handled transparently in the background. - -Gerbonara was started as an extensive refactoring of the pcb-tools_ and pcb-tools-extension_ packages. Both of these -have statement-based APIs, that is, they parse input files into one python object for every line in the file. This means -that when saving files they can recreate the input file almost byte by byte, but manipulating a file by changing -statements without breaking things is *hard*. - -Gerbonara powers gerbolyze_, a tool for converting SVG_ vector graphics files into Gerber, and embedding SVG_ into -existing Gerber files exported from a normal PCB tool for artistic purposes. - -Features -======== - - * File I/O - * Gerber, Excellon (drill file), IPC-356 (netlist) read and write - * supports file-level operations: offset, rotate, merge for all file types - * Modification API (`GraphicObject`) - * Rendering API (`GraphicPrimitive`) - * SVG export - * Full aperture macro support, including transformations (offset, rotation) - -Quick Start -=========== - -First, install gerbonara from PyPI using pip: - -.. code-block:: shell - - pip install --user gerbonara - -Then, you are ready to read and write gerber files: - -.. code-block:: python - - from gerbonara import LayerStack - - stack = LayerStack.from_directory('output/gerber') - w, h = stack.outline.size('mm') - print(f'Board size is {w:.1f} mm x {h:.1f} mm') - -Command-Line Interface -====================== - -Gerbonara comes with a `built-in command-line interface<cli-doc>` that has functions for analyzing, rendering, -modifying, and merging Gerber files. To access it, use either the ``gerbonara`` command that is part of the python -package, or run ``python -m gerbonara`` For a list of functions or help on their usage, you can use: - -.. code:: console - - $ python -m gerbonara --help - [...] - $ python -m gerbonara render --help - -Development -=========== - -Gerbonara is developed on Gitlab under the gerbolyze org: - -https://gitlab.com/gerbolyze/gerbonara/ - -A mirror of the repository can be found at: - -https://git.jaseg.de/gerbonara - -Our issue tracker is also on Gitlab: - -https://gitlab.com/gerbolyze/gerbonara/-/issues - -The documentation can be found at gitlab: - -https://gerbolyze.gitlab.io/gerbonara/ - -With Gerbonara, we aim to support as many different format variants as possible. If you have a file that Gerbonara can't -open, please file an issue on our issue tracker. Even if Gerbonara can open all your files, for regression testing we -are very interested in example files generated by any CAD or CAM tool that is not already on the list of supported -tools. - -Supported CAD Tools -=================== - -Compatibility with the output of these CAD tools is tested as part of our test suite using example files generated by -these tools. Note that not all of these tools come with default Gerber file naming rules, so YMMV if your Gerbers use -some non-standard naming convention. - - * Allegro - * Altium - * Diptrace - * Eagle - * EasyEDA - * Fritzing - * gEDA - * KiCAD - * pcb-rnd - * Siemens / Mentor Graphics Xpedition - * Siemens PADS - * Target 3001! - * Upverter - * Zuken CR-8000 - -.. _pcb-tools: https://github.com/opiopan/pcb-tools-extension -.. _pcb-tools-extension: https://github.com/curtacircuitos/pcb-tools/issues -.. _gerbolyze: https://github.com/jaseg/gerbolyze -.. _SVG: https://en.wikipedia.org/wiki/Scalable_Vector_Graphics - diff --git a/content/projects/kimesh/README.rst b/content/projects/kimesh/README.rst deleted file mode 100644 index 479690f..0000000 --- a/content/projects/kimesh/README.rst +++ /dev/null @@ -1,64 +0,0 @@ -KiCAD Security Mesh Generator -============================= - -.. image:: kicad-mesh-result-large.png - :width: 800 - :alt: A screenshot of KiCAD showing a PCB security mesh generated by KiMesh. - -This repository contains KiMesh, a KiCAD pcbnew plugin that generates security mesh traces on a KiCAD PCB. - -Installation ------------- - -KiMesh has two parts: The pcbnew plugin that generates the traces, and the magic footprints that you use to tell the -plugin how many traces of which dimensions to generate where. - -To install the plugin, copy the "kimesh" directory into your KiCAD installation's scripting plugin folder. Usually, this -is `~/.config/kicad/scripting/plugins/` for KiCAD stable installations or -`~/.config/kicad/[major version].99/scripting/plugins/` for nightly builds. On Windows, these paths can be found in your -user account's `AppData/Roaming` directory. - -To install the footprint libraries, the easiest way is to download the library zip from the project's repo -`[link] <https://git.jaseg.de/kimesh.git/plain/mesh_footprints.tar.xz?h=main>`__, unpack it to your project folder, and -add the unpacked libraries as project-specific libraries through KiCad's library management thingy. - -Usage ------ - -To work, KiMesh requires four things: - -1. An area free of other features such as footprints or traces where to generate the mesh. -2. One or more "graphic polygons" on a drawing layer that specify the area of the mesh. -3. A closed board outline on the `Edge.Cuts` layer. -4. One of the magic footprints from the KiMesh anchor library that defines the mesh's number of wires and their - dimensions, and tells KiMesh where to start the mesh and in which direction to start it. - -You can choose any layer for the outline polygons, such as the pre-defined `User.Eco1` or `User.X` layers, or you can -define your own. You just have to select that layer later in KiMesh's generator dialog. Note that KiMesh only processes -graphic polygons on that layer, and ignores other shapes such as lines, rectangles or circles. You can still use other -shapes, but you have to manually convert them to polygons before running KiMesh. To convert other shapes to -a polygon, select them, open the context menu with a right click, then choose the "Create from Selection 🞂 Create -Polygon from Selection" entry. For rectangles or circles, use the "Use Centerlines" option. For lines or arcs, use the -"Create bounding hull" option. - -Place the magic anchor footprint on the outline of the mesh's shape polygons so that you have space to route out the -traces. The anchor footprint has an arrow on the `F.Fab` layer that indicates in which direction the mesh will be -generated. - -I recommend adding the mesh to the schematic with one of KiCad's built-in `Connector_02xN_Top_Bottom` footprints. For a -mesh with k wires, choose a symbol with two rows of 2k pins each. For instance, for two mesh wires, choose -`Connector_02x04_Top_bottom`. Then assign one of the magic footprints to that symbol. To avoid DRC warnings, join the -two halves of the mesh on the output side of the footprint. That's the right side in default orientation, where the -higher-numbered pins are. - -.. image:: screenshot-mesh-schematic.png - :width: 800 - :alt: A screenshot of the connector footprint mentioned in the previous paragraph, shown conencted up as described in - KiCad's schematic editor. - -Theory of operation -------------------- - -I have published a post_ on my blog on the theory of operation behind KiMesh. - -.. _post: https://blog.jaseg.de/posts/kicad-mesh-plugin/ diff --git a/content/projects/kimesh/index.rst b/content/projects/kimesh/index.rst deleted file mode 100644 index 8613e3c..0000000 --- a/content/projects/kimesh/index.rst +++ /dev/null @@ -1,16 +0,0 @@ ---- -title: "KiMesh" -date: 2023-10-04T23:42:00+02:00 -external_links: - - name: Sources - url: "https://git.jaseg.de/kimesh.git" - - name: Issues - url: "https://github.com/jaseg/kimesh/issues" - - name: Docs - url: "https://jaseg.de/projects/kimesh" -summary: > - KiMesh is a KiCad plugin that automatically creates security meshes with two or traces covering an - arbitrarily-shaped outline on the board. ---- - -.. include:: content/projects/kimesh/README.rst diff --git a/content/projects/lolcat-c/index.rst b/content/projects/lolcat-c/index.rst deleted file mode 100644 index 858f6de..0000000 --- a/content/projects/lolcat-c/index.rst +++ /dev/null @@ -1,107 +0,0 @@ ---- -title: "lolcat-c" -external_links: - - name: Sources - url: "https://git.jaseg.de/lolcat.git" - - name: Github - url: "https://github.com/jaseg/lolcat" - - name: Issues - url: "https://github.com/jaseg/lolcat/issues" -summary: > - lolcat-c is a small, high-performance re-implementation of the - `lolcat <https://github.com/busyloop/lolcat>`__ - rainbow cat utility. lolcat-c is meant as a lolcat that you can actually use in production. It is fast, not slowing - down whatever you pipe through it, and it robustly handles real-world terminal output including escape sequences. ---- - -What? -===== - -.. image:: LOLCat-Rainbow.jpg - -Screenshot -========== - -.. image:: screenshot.png - -.. image:: sl.gif - -Installation -============ - -Archlinux ---------- - -There's an `AUR package <https://aur.archlinux.org/packages/c-lolcat>`__: - -.. code:: sh - - $ git clone https://aur.archlinux.org/packages/c-lolcat - $ cd c-lolcat - $ makepkg -csi - -Fedora ------- - -.. code:: sh - - $ dnf install lolcat - -Ubuntu (Snap) -------------- - -See `this awesome blog post by a kind person from the internet <https://blog.simos.info/how-to-make-a-snap-package-for-lolcat-with-snapcraft-on-ubuntu/>`__: - -.. code:: sh - - $ snap install lolcat-c - -Mac ---- - -Build loclcat with: - -.. code:: sh - - $ make lolcat - -...and put the resulting binary at a place of your choice. - -Others ------- - -.. code:: sh - - $ make && sudo make install - -Why? -==== - -This `lolcat` clone is an attempt to reduce the world's carbon dioxide emissions by optimizing inefficient code. It's ->10x as fast and <0.1% as large as the original one. - -.. code:: sh - - newton~/d/lolcat <3 dmesg>foo - newton~/d/lolcat <3 time upstream/bin/lolcat foo - 13.51user 1.34system 0:15.99elapsed 92%CPU (0avgtext+0avgdata 10864maxresident)k - 0inputs+0outputs (0major+1716minor)pagefaults 0swaps - newton~/d/lolcat <3 time ./lolcat foo - 0.02user 0.00system 0:00.09elapsed 34%CPU (0avgtext+0avgdata 1936maxresident)k - 0inputs+0outputs (0major+117minor)pagefaults 0swaps - -Bonus comparison with `python-lolcat <https://github.com/tehmaze/lolcat/>`__: - -.. code:: sh - - newton~/d/lolcat <3 dmesg>foo - $ time python-lolcat foo - 12.27user 0.00system 0:12.29elapsed 99%CPU (0avgtext+0avgdata 11484maxresident)k - 0inputs+0outputs (0major+1627minor)pagefaults 0swaps - $ time c-lolcat foo - 0.29user 0.00system 0:00.30elapsed 98%CPU (0avgtext+0avgdata 468maxresident)k - 0inputs+0outputs (0major+21minor)pagefaults 0swaps - -(Read: `c-lolcat << python-lolcat << ruby-lolcat`) - - diff --git a/content/projects/python-mpv/README.rst b/content/projects/python-mpv/README.rst deleted file mode 100644 index 26815d1..0000000 --- a/content/projects/python-mpv/README.rst +++ /dev/null @@ -1,401 +0,0 @@ -.. vim: tw=120 sw=4 et - -python-mpv is a ctypes-based python interface to the mpv media player. It gives you more or less full control of all -features of the player, just as the lua interface does. - -Installation ------------- - -.. code:: bash - - pip install mpv - - -...though you can also realistically just copy `mpv.py`_ into your project as it's all nicely contained in one file. - -Requirements -~~~~~~~~~~~~ - -libmpv -...... -``libmpv.so`` either locally (in your current working directory) or somewhere in your system library search path. This -module is somewhat lenient as far as ``libmpv`` versions are concerned but since ``libmpv`` is changing quite frequently -you'll only get all the newest features when using an up-to-date version of this module. The unit tests for this module -do some basic automatic version compatibility checks. If you discover anything missing here, please open an `issue`_ or -submit a `pull request`_ on github. - -On Windows you can place libmpv anywhere in your ``%PATH%`` (e.g. next to ``python.exe``) or next to this module's -``mpv.py``. Before falling back to looking in the mpv module's directory, python-mpv uses the DLL search order built -into ctypes, which is different to the one Windows uses internally. Consult `this stackoverflow post -<https://stackoverflow.com/a/23805306>`__ for details. - -Python >= 3.7 (officially) -.......................... -The ``main`` branch officially only supports recent python releases (3.5 onwards), but there is the somewhat outdated -but functional `py2compat branch`_ providing Python 2 compatibility. - -.. _`py2compat branch`: https://github.com/jaseg/python-mpv/tree/py2compat -.. _`issue`: https://github.com/jaseg/python-mpv/issues -.. _`pull request`: https://github.com/jaseg/python-mpv/pulls - -Supported Platforms -................... - -**Linux**, **Windows** and **OSX** all seem to work mostly fine. For some notes on the installation on Windows see -`this comment`__. Shared library handling is quite bad on windows, so expect some pain there. On OSX there seems to be -some bug int the event logic. See `issue 36`_ and `issue 61`_ for details. Creating a pyQT window and having mpv draw -into it seems to be a workaround (about 10loc), but in case you want this fixed please weigh in on the issue tracker -since right now there is not many OSX users. - -.. __: https://github.com/jaseg/python-mpv/issues/60#issuecomment-352719773 -.. _`issue 61`: https://github.com/jaseg/python-mpv/issues/61 -.. _`issue 36`: https://github.com/jaseg/python-mpv/issues/36 - -Usage ------ - -.. code:: python - - import mpv - player = mpv.MPV(ytdl=True) - player.play('https://youtu.be/DOmdB7D-pUU') - player.wait_for_playback() - -python-mpv mostly exposes mpv's built-in API to python, adding only some porcelain on top. Most "`input commands <https://mpv.io/manual/master/#list-of-input-commands>`_" are mapped to methods of the MPV class. Check out these methods and their docstrings in `the source <https://github.com/jaseg/python-mpv/blob/main/mpv.py>`__ for things you can do. Additional controls and status information are exposed through `MPV properties <https://mpv.io/manual/master/#properties>`_. These can be accessed like ``player.metadata``, ``player.fullscreen`` and ``player.loop_playlist``. - -Threading -~~~~~~~~~ - -The ``mpv`` module starts one thread for event handling, since MPV sends events that must be processed quickly. The -event queue has a fixed maxmimum size and some operations can cause a large number of events to be sent. - -If you want to handle threading yourself, you can pass ``start_event_thread=False`` to the ``MPV`` constructor and -manually call the ``MPV`` object's ``_loop`` function. If you have some strong need to not use threads and use some -external event loop (such as asyncio) instead you can do that, too with some work. The API of the backend C ``libmpv`` -has a function for producing a sort of event file descriptor for a handle. You can use that to produce a file descriptor -that can be passed to an event loop to tell it to wake up the python-mpv event handler on every incoming event. - -All API functions are thread-safe. If one is not, please file an issue on github. - -Advanced Usage -~~~~~~~~~~~~~~ - -Logging, Properties, Python Key Bindings, Screenshots and youtube-dl -.................................................................... - -.. code:: python - - #!/usr/bin/env python3 - import mpv - - def my_log(loglevel, component, message): - print('[{}] {}: {}'.format(loglevel, component, message)) - - player = mpv.MPV(log_handler=my_log, ytdl=True, input_default_bindings=True, input_vo_keyboard=True) - - # Property access, these can be changed at runtime - @player.property_observer('time-pos') - def time_observer(_name, value): - # Here, _value is either None if nothing is playing or a float containing - # fractional seconds since the beginning of the file. - print('Now playing at {:.2f}s'.format(value)) - - player.fullscreen = True - player.loop_playlist = 'inf' - # Option access, in general these require the core to reinitialize - player['vo'] = 'gpu' - - @player.on_key_press('q') - def my_q_binding(): - print('THERE IS NO ESCAPE') - - @player.on_key_press('s') - def my_s_binding(): - pillow_img = player.screenshot_raw() - pillow_img.save('screenshot.png') - - player.play('https://youtu.be/DLzxrzFCyOs') - player.wait_for_playback() - - del player - -Skipping silence using libav filters -.................................... - -The following code uses the libav silencedetect filter to skip silence at the beginning of a file. It works by loading -the filter, then parsing its output from mpv's log. Thanks to Sean DeNigris on github (#202) for the original code! - -.. code:: python - - #!/usr/bin/env python3 - import sys - import mpv - - p = mpv.MPV() - p.play(sys.argv[1]) - - def skip_silence(): - p.set_loglevel('debug') - p.af = 'lavfi=[silencedetect=n=-20dB:d=1]' - p.speed = 100 - def check(evt): - toks = evt['event']['text'].split() - if 'silence_end:' in toks: - return float(toks[2]) - p.time_pos = p.wait_for_event('log_message', cond=check) - p.speed = 1 - p.af = '' - - skip_silence() - p.wait_for_playback() - -Video overlays -.............. - -.. code:: python - - #!/usr/bin/env python3 - import time - from PIL import Image, ImageDraw, ImageFont - import mpv - - player = mpv.MPV() - - player.loop = True - player.play('test.webm') - player.wait_until_playing() - - font = ImageFont.truetype('DejaVuSans.ttf', 40) - - while not player.core_idle: - - time.sleep(0.5) - overlay = player.create_image_overlay() - - for pos in range(0, 500, 5): - ts = player.time_pos - if ts is None: - break - - img = Image.new('RGBA', (400, 150), (255, 255, 255, 0)) - d = ImageDraw.Draw(img) - d.text((10, 10), 'Hello World', font=font, fill=(0, 255, 255, 128)) - d.text((10, 60), f't={ts:.3f}', font=font, fill=(255, 0, 255, 255)) - - overlay.update(img, pos=(2*pos, pos)) - time.sleep(0.05) - - overlay.remove() - - -Playlist handling -................. - -.. code:: python - - #!/usr/bin/env python3 - import mpv - - player = mpv.MPV(ytdl=True, input_default_bindings=True, input_vo_keyboard=True) - - player.playlist_append('https://youtu.be/PHIGke6Yzh8') - player.playlist_append('https://youtu.be/Ji9qSuQapFY') - player.playlist_append('https://youtu.be/6f78_Tf4Tdk') - - player.playlist_pos = 0 - - while True: - # To modify the playlist, use player.playlist_{append,clear,move,remove}. player.playlist is read-only - print(player.playlist) - player.wait_for_playback() - -Directly feeding mpv data from python -..................................... - -.. code:: python - - #!/usr/bin/env python3 - import mpv - - player = mpv.MPV() - @player.python_stream('foo') - def reader(): - with open('test.webm', 'rb') as f: - while True: - yield f.read(1024*1024) - - player.play('python://foo') - player.wait_for_playback() - -Using external subtitles -........................ - -The easiest way to load custom subtitles from a file is to pass the ``--sub-file`` option to the ``loadfile`` call: - -.. code:: python - - #!/usr/bin/env python3 - import mpv - - player = mpv.MPV() - player.loadfile('test.webm', sub_file='test.srt') - player.wait_for_playback() - -Note that you can also pass many other options to ``loadfile``. See the mpv docs for details. - -If you want to add subtitle files or streams at runtime, you can use the ``sub-add`` command. ``sub-add`` can only be -called once the player is done loading the file and starts playing. An easy way to wait for this is to wait for the -``core-idle`` property. - -.. code:: python - - #!/usr/bin/env python3 - import mpv - - player = mpv.MPV() - player.play('test.webm') - player.wait_until_playing() - player.sub_add('test.srt') - player.wait_for_playback() - -Using MPV's built-in GUI -........................ - -python-mpv is using mpv via libmpv. libmpv is meant for embedding into other applications and by default disables most -GUI features such as the OSD or keyboard input. To enable the built-in GUI, use the following options when initializing -the MPV instance. See `Issue 102`_ for more details - -.. _`issue 102`: https://github.com/jaseg/python-mpv/issues/61 - -.. code:: python - - # Enable the on-screen controller and keyboard shortcuts - player = mpv.MPV(input_default_bindings=True, input_vo_keyboard=True, osc=True) - - # Alternative version using the old "floating box" style on-screen controller - player = mpv.MPV(player_operation_mode='pseudo-gui', - script_opts='osc-layout=box,osc-seekbarstyle=bar,osc-deadzonesize=0,osc-minmousemove=3', - input_default_bindings=True, - input_vo_keyboard=True, - osc=True) - -PyQT embedding -.............. - -.. code:: python - - #!/usr/bin/env python3 - import mpv - import sys - - from PyQt5.QtWidgets import * - from PyQt5.QtCore import * - - class Test(QMainWindow): - def __init__(self, parent=None): - super().__init__(parent) - self.container = QWidget(self) - self.setCentralWidget(self.container) - self.container.setAttribute(Qt.WA_DontCreateNativeAncestors) - self.container.setAttribute(Qt.WA_NativeWindow) - player = mpv.MPV(wid=str(int(self.container.winId())), - vo='x11', # You may not need this - log_handler=print, - loglevel='debug') - player.play('test.webm') - - app = QApplication(sys.argv) - - # This is necessary since PyQT stomps over the locale settings needed by libmpv. - # This needs to happen after importing PyQT before creating the first mpv.MPV instance. - import locale - locale.setlocale(locale.LC_NUMERIC, 'C') - win = Test() - win.show() - sys.exit(app.exec_()) - -PyGObject embedding -................... - -.. code:: python - - #!/usr/bin/env python3 - import gi - - import mpv - - gi.require_version('Gtk', '3.0') - from gi.repository import Gtk - - - class MainClass(Gtk.Window): - - def __init__(self): - super(MainClass, self).__init__() - self.set_default_size(600, 400) - self.connect("destroy", self.on_destroy) - - widget = Gtk.Frame() - self.add(widget) - self.show_all() - - # Must be created >after< the widget is shown, else property 'window' will be None - self.mpv = mpv.MPV(wid=str(widget.get_property("window").get_xid())) - self.mpv.play("test.webm") - - def on_destroy(self, widget, data=None): - self.mpv.terminate() - Gtk.main_quit() - - - if __name__ == '__main__': - # This is necessary since like Qt, Gtk stomps over the locale settings needed by libmpv. - # Like with Qt, this needs to happen after importing Gtk but before creating the first mpv.MPV instance. - import locale - locale.setlocale(locale.LC_NUMERIC, 'C') - - application = MainClass() - Gtk.main() - -Using OpenGL from PyGObject -........................... - -Just like it is possible to render into a GTK widget through X11 windows, it `also is possible to render into a GTK -widget using OpenGL <https://gist.github.com/jaseg/657e8ecca3267c0d82ec85d40f423caa>`__ through this python API. - -Using OpenGL from PyQt5/QML -........................... - -Robozman_ has mangaed to `make mpv render into a PyQt5/QML widget using OpenGL -<https://gitlab.com/robozman/python-mpv-qml-example>`__ through this python API. - -Using mpv inside imgui inside OpenGL via GLFW -............................................. - -dfaker_ has written a demo (`link <https://github.com/dfaker/imgui_glfw_pythonmpv_demo/blob/main/main.py>`__) that uses mpv to render video into an `imgui <https://github.com/ocornut/imgui>`__ UI running on an OpenGL context inside `GLFW <https://www.glfw.org/>`__. Check out their demo to see how to integrate with imgui/OpenGL and how to access properties and manage the lifecycle of an MPV instance. - -Running tests -------------- - -Use pytest to run tests. - -Coding Conventions ------------------- - -The general aim is `PEP 8`_, with liberal application of the "consistency" section. 120 cells line width. Four spaces. -No tabs. Probably don't bother making pure-formatting PRs except if you think it *really* helps readability or it -*really* irks you if you don't. - -License -------- - -python-mpv inherits the underlying libmpv's license, which can be either GPLv2 or later (default) or LGPLv2.1 or later. -For details, see `the mpv copyright page`_. - -.. _`PEP 8`: https://www.python.org/dev/peps/pep-0008/ -.. _`mpv.py`: https://raw.githubusercontent.com/jaseg/python-mpv/main/mpv.py -.. _cosven: https://github.com/cosven -.. _Robozman: https://gitlab.com/robozman -.. _dfaker: https://github.com/dfaker -.. _`the mpv copyright page`: https://github.com/mpv-player/mpv/blob/master/Copyright - diff --git a/content/projects/python-mpv/index.rst b/content/projects/python-mpv/index.rst deleted file mode 100644 index 12b1c18..0000000 --- a/content/projects/python-mpv/index.rst +++ /dev/null @@ -1,18 +0,0 @@ ---- -title: "python-mpv" -external_links: - - name: Sources - url: "https://git.jaseg.de/python-mpv.git" - - name: Issues - url: "https://github.com/jaseg/python-mpv/issues" - - name: Docs - url: "https://neinseg.gitlab.io/python-mpv" - - name: PyPI - url: "https://pypi.org/project/mpv" -summary: > - python-mpv is a small, ctypes-based Python library wrapping the libmpv media player library. Despite its small size - and simple API, python-mpv allows advanced control over libmpv and beyond simple remote control of mpv can be used - to embed mpv in OpenGL, Qt, and GTK-based Python applications. ---- - -.. include:: content/projects/python-mpv/README.rst diff --git a/content/projects/svg-flatten/index.rst b/content/projects/svg-flatten/index.rst deleted file mode 100644 index ae9a8f3..0000000 --- a/content/projects/svg-flatten/index.rst +++ /dev/null @@ -1,22 +0,0 @@ ---- -title: "svg-flatten" -external_links: - - name: Sources - url: "https://git.jaseg.de/gerbolyze.git/tree/svg-flatten?h=main" - - name: Issues - url: "https://github.com/jaseg/gerbolyze/issues" - - name: Docs - url: "https://gerbolyze.gitlab.io/svg-flatten" -summary: > - svg-flatten is a command-line utility that performs vector occlusion and clipping on SVG files, producing a - flattened SVG file without overlapping elements, without changing what the file looks like. svg-flatten is used as a - part of gerbolyze. ---- - -svg-flatten is a command-line utility that performs vector occlusion and clipping on SVG files, producing a flattened -SVG file without overlapping elements, without changing what the file looks like. svg-flatten is used as a part of -gerbolyze. - -I developed svg-flatten as part of gerbolyze_, but it can be used independently. - -.. _gerbolyze: {{< ref "projects/gerbolyze" >}} diff --git a/content/projects/wsdiff/index.rst b/content/projects/wsdiff/index.rst deleted file mode 100644 index 44cd4c3..0000000 --- a/content/projects/wsdiff/index.rst +++ /dev/null @@ -1,63 +0,0 @@ ---- -title: "wsdiff" -external_links: - - name: Sources - url: "https://git.jaseg.de/wsdiff.git" - - name: Issues - url: "https://github.com/jaseg/wsdiff/issues" - - name: PyPI - url: "https://pypi.org/project/wsdiff" -summary: > - wsdiff is a command-line utility that produces self-contained, syntax-highlighted, HTML-formatted diffs that support - both unified and side-by-side diffs from a single source file using nothing but CSS magic. ---- - -wsdiff is a python script that produces a diff of two files or directories as a single, self-contained HTML file. The -resulting diff works without Javascript and will automatically switch between inline and side-by-side formats depending -on available screen space. - -Installation -============ - -.. code:: sh - - $ pip install wsdiff - -Usage -===== - -:: - - usage: wsdiff [-h] [-b] [-s SYNTAX_CSS] [-l LEXER] [-L] [-t PAGETITLE] - [-o OUTPUT] [--header] [--content] - [old] [new] - - Given two source files or directories this application creates an html page - that highlights the differences between the two. - - positional arguments: - old source file or directory to compare ("before" file) - new source file or directory to compare ("after" file) - - options: - -h, --help show this help message and exit - -b, --open Open output file in a browser - -s SYNTAX_CSS, --syntax-css SYNTAX_CSS - Path to custom Pygments CSS file for code syntax - highlighting - -l LEXER, --lexer LEXER - Manually select pygments lexer (default: guess from - filename, use -L to list available lexers.) - -L, --list-lexers List available lexers for -l/--lexer - -t PAGETITLE, --pagetitle PAGETITLE - Override page title of output HTML file - -o OUTPUT, --output OUTPUT - Name of output file (default: stdout) - --header Only output HTML header with stylesheets and stuff, - and no diff - --content Only output HTML content, without header - -Example Output -============== - -.. image:: latest.png diff --git a/deploy.py b/deploy.py deleted file mode 100644 index c960fbc..0000000 --- a/deploy.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python3 - -import os -import tempfile -import subprocess -from pathlib import Path - -if __name__ == '__main__': - with tempfile.TemporaryDirectory() as tmpdir: - - current_branch = subprocess.run(['git', 'symbolic-ref', '-q', 'HEAD'], check=False, capture_output=True).stdout.strip() - if current_branch == 'refs/heads/deploy': - raise SystemError('This script cannot be run from the deploy branch (run from main instead)') - - subprocess.run(['git', 'worktree', 'add', '--detach', tmpdir], check=True) - try: - env = dict(os.environ) - env['PATH'] = f'{Path("hack").absolute()}:{env["PATH"]}' - subprocess.run(['hugo'], cwd=tmpdir, check=True, env=env) - subprocess.run(['git', 'add', '--force', 'public'], cwd=tmpdir, check=True) - write_tree = subprocess.run(['git', 'write-tree', '--prefix=public/'], cwd=tmpdir, check=True, capture_output=True) - tree = write_tree.stdout.strip() - - commit = subprocess.run(['git', 'commit-tree', '-p', 'HEAD', '-p', 'refs/heads/deploy', '-m', 'deploy.py auto-commit', tree], cwd=tmpdir, check=True, capture_output=True).stdout.strip() - subprocess.run(['git', 'update-ref', '-m', f'deploy.sh update deploy branch to {commit}', 'refs/heads/deploy', commit], cwd=tmpdir, check=True) - - subprocess.run(['git', 'push', 'git@git.jaseg.de:blog.git', 'deploy'], cwd=tmpdir, check=True) - finally: - subprocess.run(['git', 'worktree', 'remove', '-f', tmpdir], check=True) diff --git a/docutils.conf b/docutils.conf deleted file mode 100644 index 308c0f7..0000000 --- a/docutils.conf +++ /dev/null @@ -1,3 +0,0 @@ -[restructuredtext parser] -syntax_highlight: short - diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Medium.ttf b/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Medium.ttf Binary files differindex 18728fc..18728fc 100644 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Medium.ttf +++ b/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Medium.ttf diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-MediumItalic.ttf b/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-MediumItalic.ttf Binary files differindex 30e82bd..30e82bd 100644 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-MediumItalic.ttf +++ b/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-MediumItalic.ttf diff --git a/themes/conspiracy/assets/fonts/fira_code/FiraCode-VariableFont_wght.ttf b/fonts/fira_code/FiraCode-VariableFont_wght.ttf Binary files differindex f75b2a2..f75b2a2 100644 --- a/themes/conspiracy/assets/fonts/fira_code/FiraCode-VariableFont_wght.ttf +++ b/fonts/fira_code/FiraCode-VariableFont_wght.ttf diff --git a/themes/conspiracy/assets/fonts/manuskript_gothisch/Manuskript Gothisch UNZ1A.ttf b/fonts/manuskript_gothisch/Manuskript Gothisch UNZ1A.ttf Binary files differindex f90ba01..f90ba01 100644 --- a/themes/conspiracy/assets/fonts/manuskript_gothisch/Manuskript Gothisch UNZ1A.ttf +++ b/fonts/manuskript_gothisch/Manuskript Gothisch UNZ1A.ttf diff --git a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2 b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2 Binary files differindex 8e28860..8e28860 100644 --- a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2 +++ b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2 diff --git a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2 b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2 Binary files differindex c4a12e0..c4a12e0 100644 --- a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2 +++ b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2 diff --git a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Dark.woff2 b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Dark.woff2 Binary files differindex c255d39..c255d39 100644 --- a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Dark.woff2 +++ b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Dark.woff2 diff --git a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-DarkItalic.woff2 b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-DarkItalic.woff2 Binary files differindex 7e0da32..7e0da32 100644 --- a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-DarkItalic.woff2 +++ b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-DarkItalic.woff2 diff --git a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Light.woff2 b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Light.woff2 Binary files differindex 418f03e..418f03e 100644 --- a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Light.woff2 +++ b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Light.woff2 diff --git a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-LightItalic.woff2 b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-LightItalic.woff2 Binary files differindex b2d3ab9..b2d3ab9 100644 --- a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-LightItalic.woff2 +++ b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-LightItalic.woff2 diff --git a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Medium.woff2 b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Medium.woff2 Binary files differindex 42d677d..42d677d 100644 --- a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Medium.woff2 +++ b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Medium.woff2 diff --git a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-MediumItalic.woff2 b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-MediumItalic.woff2 Binary files differindex b092803..b092803 100644 --- a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-MediumItalic.woff2 +++ b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-MediumItalic.woff2 diff --git a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2 b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2 Binary files differindex 5d518dc..5d518dc 100644 --- a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2 +++ b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2 diff --git a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-RegularItalic.woff2 b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-RegularItalic.woff2 Binary files differindex 79adbb6..79adbb6 100644 --- a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-RegularItalic.woff2 +++ b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-RegularItalic.woff2 diff --git a/themes/conspiracy/assets/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf b/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf Binary files differindex c7204ce..c7204ce 100644 --- a/themes/conspiracy/assets/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf +++ b/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf diff --git a/hack/rst2html b/hack/rst2html deleted file mode 100755 index e3a390d..0000000 --- a/hack/rst2html +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python3 -# https://gist.github.com/mastbaum/2655700 - -import sys -import re - -import docutils.core -from docutils.transforms import Transform -from docutils.nodes import TextElement, Inline, Text -from docutils.parsers.rst import Directive, directives -from docutils.writers.html4css1 import Writer, HTMLTranslator - - -class UnfuckedHTMLTranslator(HTMLTranslator): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.in_literal_block = False - - def visit_literal_block(self, node): - self.in_literal_block = True - self.body.append(self.starttag(node, 'pre', CLASS='literal-block')) - self.body.append('<span class="lineno"></span><span class="line">') - - def depart_literal_block(self, node): - self.in_literal_block = False - self.body.append('\n</span></pre>\n') - - def visit_Text(self, node): - if self.in_literal_block: - for match in re.finditer('([^\n]*)(\n|$)', node.astext()): - text, end = match.groups() - - if text: - super().visit_Text(Text(text)) - - if end == '\n': - if isinstance(node.parent, Inline): - self.depart_inline(node.parent) - self.body.append(f'</span>\n<span class="lineno"></span><span class="line">') - if isinstance(node.parent, Inline): - self.visit_inline(node.parent) - - else: - super().visit_Text(node) - - -html_writer = Writer() -html_writer.translator_class = UnfuckedHTMLTranslator -docutils.core.publish_cmdline(writer=html_writer) - diff --git a/themes/conspiracy/assets/images/fabrizio-conti-TUmjK7ZJgbI-unsplash.jpg b/images/fabrizio-conti-TUmjK7ZJgbI-unsplash.jpg Binary files differindex 767741f..767741f 100644 --- a/themes/conspiracy/assets/images/fabrizio-conti-TUmjK7ZJgbI-unsplash.jpg +++ b/images/fabrizio-conti-TUmjK7ZJgbI-unsplash.jpg diff --git a/imprint/index.html b/imprint/index.html new file mode 100644 index 0000000..30e3605 --- /dev/null +++ b/imprint/index.html @@ -0,0 +1,130 @@ +<!DOCTYPE html> +<html><head> + <meta charset="utf-8"> + <title>Impressum | Home</title> + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="mobile-web-app-capable" content="yes"> + <meta name="color-scheme" content="dark light"> + <link rel="stylesheet" href="/style.css"> +</head> +<body><nav> + <div class="internal"> + + <a href="/" title="Home">Home</a> + <a href="/blog/" title="Blog">Blog</a> + <a href="/projects/" title="Projects">Projects</a> + <a href="/about/" title="About">About</a> + </div> + <div class="external"> + <a href="https://git.jaseg.de/" title="cgit">cgit</a> + <a href="https://github.com/jaseg" title="Github">Github</a> + <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a> + <a href="https://chaos.social/jaseg" title="Mastodon">Mastodon</a> + </span> +</nav> + + <header> + <h1>Impressum</h1> +<ul class="breadcrumbs"> + <li><a href="/">jaseg.de</a></li><li><a href="/imprint/">Impressum</a></li> +</ul> + + </header> + <main> + <div class="document"> + + +<div class="section" id="impressum"> +<h2>Impressum</h2> +<p> +Sebastian Götte<br/> +c/o Praxis Dr. Götte<br/> +Krankenhausstr. 1a<br/> +54634 Bitburg<br/> +imprint@jaseg.net +</p> + +Inhaltlich Verantwortlicher gem. § 55 II RStV: Sebastian Götte (Anschrift s.o.)</div> +<div class="section" id="lizenz-dieser-seite"> +<h2>Lizenz dieser Seite</h2> +Dieses Werk ist lizenziert unter einer +<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"> + Creative Commons Namensnennung - Nicht-kommerziell - Weitergabe unter gleichen Bedingungen 4.0 International + Lizenz (CC-BY-NC-SA) +</a>.<br/> +<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"> + <img alt="Creative Commons Lizenzvertrag" style="border-width:0" + src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /> +</a></div> +<div class="section" id="haftungsbeschrankung-fur-inhalte-der-website"> +<h2>Haftungsbeschränkung für Inhalte der Website</h2> +<p>Gemäß § 7 Abs. 1 TMG ist der Verantwortliche der Website i. S. v. § 5 TMG für eigene Informationen, die er zur Nutzung +bereithält, nach den allgemeinen Gesetzen verantwortlich.</p> +<p>Der Verantwortliche der vorbezeichneten Website übernimmt keine Haftung auf Aktualität, Richtigkeit und Vollständigkeit +der auf dieser Website zur Verfügung gestellten Inhalte. Dies gilt nicht, wenn dem Verantwortlichen vorsätzliches oder +grob fahrlässiges Verhalten vorzuwerfen ist. Die Inhalte wurden mit der größtmöglichen Sorgfalt erstellt. Dennoch kann +die inhaltliche Richtigkeit insbesondere bei komplexen Themen nicht gewährleistet werden, so dass der Verantwortliche +den Nutzern empfiehlt, bei wichtigen Informationen bei den zuständigen Stellen anzufragen oder rechtliche Beratung in +Anspruch zu nehmen. Sofern kostenpflichtige Inhalte oder Dienste auf der Website zur Verfügung gestellt werden, handelt +es sich dabei um unverbindliche Invitatio ad offerendum, welche lediglich zur Abgabe eines Angebots durch den Nutzer +aufrufen und selbst kein verbindliches Angebot darstellen.</p> +<p>Gemäß §§ 8 ff. TMG ist der Website-Betreiber für fremde Inhalte, die er für einen Nutzer veröffentlicht, nicht +verantwortlich, sofern</p> +<ul class="simple"> +<li>er keine Kenntnis von der rechtswidrigen Handlung oder der Information hat und ihm im Falle von +Schadensersatzansprüchen auch keine Tatsachen oder Umstände bekannt sind, aus denen die rechtswidrige Handlung oder +die Information offensichtlich wird, oder</li> +<li>er unverzüglich tätig geworden ist, um die Information zu entfernen oder den Zugang zu ihr zu sperren, sobald er +diese Kenntnis erlangt hat.</li> +</ul> +<p>Erlangt der Website-Betreiber Kenntnis von solchen rechtswidrigen Inhalten, werden diese unverzüglich entfernt.</p> +</div> +<div class="section" id="haftung-fur-ausgehende-links"> +<h2>Haftung für ausgehende Links</h2> +<p>Der Website-Betreiber verlinkt von seiner Website auf fremde Websites. Durch diese sog. „Hyperlinks“ wird der Nutzer +direkt auf die fremde Website geleitet. Der Website-Betreiber hat dabei keinerlei Einfluss auf die Informationen der +fremden Website. Daher kann keine Haftung für die Aktualität, Richtigkeit und Vollständigkeit der Inhalte der fremden +Website übernommen werden. Der Website-Betreiber versichert jedoch, dass ihm zum Zeitpunkt des Setzens der Verlinkung +keinerlei rechtliche Verstöße bekannt waren und er die fremde Website im Rahmen des Zumutbaren geprüft hat.</p> +<p>Erhält der Website-Betreiber Kenntnis von der Rechtswidrigkeit der verlinkten Inhalte, wird der entsprechende Link +unverzüglich entfernt.</p> +</div> +<div class="section" id="urheberrechte"> +<h2>Urheberrechte</h2> +<p>Die auf dieser Webseite veröffentlichten Inhalte unterliegen dem deutschen Urheberrecht.</p> +<p>Als Urheber i. S. v. § 7 UrhG stehen dem Website-Betreiber die alleinigen und ausschließlichen Verwertungsrechte gemäß +§§ 15 ff. UrhG zu. Eine Vervielfältigung oder Verwendung sämtlicher Inhalte der Website in fremden elektronischen oder +gedruckten Medien ist ohne vorherige Zustimmung des Website-Betreibers untersagt und wird ggf. verfolgt.</p> +</div> +<div class="section" id="datenschutz"> +<h2>Datenschutz</h2> +<p>Die Website kann grundsätzlich ohne Eingabe von personenbezogenen Daten wie z. B. Name oder E-Mail-Adresse genutzt +werden. Sollte die Möglichkeit der Eingabe solcher Daten bestehen, so erfolgt die Mitteilung dieser Daten auf +freiwilliger Basis durch den Nutzer. Eine Weitergabe dieser Daten an Dritte ist ohne die ausdrückliche Zustimmung durch +den Nutzer ausgeschlossen.</p> +<p>Ein lückenloser Schutz der übermittelten Daten vor dem Zugriff durch unbefugte Dritte ist jedoch nicht möglich, da es im +Bereich der Datenübermittlung im Internet zu Sicherheitslücken kommen kann. Der Website-Betreiber versucht jedoch, die +Gefahr des unbefugten Zugriffs durch geeignete Maßnahmen zu unterbinden und im Falle der Kenntnis von einer +Sicherheitslücke diese durch geeignete Maßnahmen zu schließen.</p> +<p>Der Nutzung der im Impressum veröffentlichten Kontaktdaten durch Dritte zur Übersendung von nicht ausdrücklich +gewünschter Werbung, insbesondere Spam-Mails, wird widersprochen und im Falle der Nichtbeachtung ggf. mit rechtlichen +Schritten verfolgt.</p> +<p>(Quelle: <a class="reference external" href="https://www.kanzlei-wirtschaftsrecht.berlin/aktuelles/kostenloser-muster-disclaimer">BUSE HERZ GRUNST Rechtsanwälte</a>)</p> +</div> +</div> + </main><footer> + Copyright © 2023 Jan Sebastian Götte + / <a href="/about/">About</a> + / <a href="/imprint/">Imprint</a> +</footer> +<script> + if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser + contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to + prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition. + In support of an open web, this website does not function with this DRM. Please install a browser such + as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports + ad blockers.`; + </script> + </body> +</html> diff --git a/index.html b/index.html new file mode 100644 index 0000000..2489254 --- /dev/null +++ b/index.html @@ -0,0 +1,184 @@ +<!DOCTYPE html> +<html><head> + <meta name="generator" content="Hugo 0.119.0"> + <meta charset="utf-8"> + <title>Home</title> + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="mobile-web-app-capable" content="yes"> + <meta name="color-scheme" content="dark light"> + <link rel="stylesheet" href="/style.css"> +</head> +<body><nav> + <div class="internal"> + + <a href="/" title="Home" class="active">Home</a> + <a href="/blog/" title="Blog">Blog</a> + <a href="/projects/" title="Projects">Projects</a> + <a href="/about/" title="About">About</a> + </div> + <div class="external"> + <a href="https://git.jaseg.de/" title="cgit">cgit</a> + <a href="https://github.com/jaseg" title="Github">Github</a> + <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a> + <a href="https://chaos.social/jaseg" title="Mastodon">Mastodon</a> + </span> +</nav> + + <header> + <h1>jaseg.de</h1> + </header> + <main class="cards"> + <div class="intro"> + <div class="document"> + + +<p>Hi there, and welcome to my personal website.</p> +<p>I'm jaseg, and I write about my projects here. You can find long-form articles in the blog, and links to my open-source +projects on the projects page. On the top right of this page, there are links to my git repositories and social media +pages. If you want to learn more about me, head over to the about page.</p> +</div> + </div> + <h2>Recently updated projects</h2> + <div class="card"><h3><a href="/projects/kimesh/">KiMesh</a></h3> + + <div class="summary"> + <div class="document"> + + +<p>KiMesh is a KiCad plugin that automatically creates security meshes with two or traces covering an arbitrarily-shaped outline on the board.</p> +</div> + <a href="http://jaseg.de/projects/kimesh/">Read more</a> + </div> + <div class="links"> + <a href="https://git.jaseg.de/kimesh.git">Sources</a> + <a href="https://github.com/jaseg/kimesh/issues">Issues</a> + <a href="https://jaseg.de/projects/kimesh">Docs</a> + </div> +</div> + + <div class="card"><h3><a href="/projects/gerbolyze/">Gerbolyze</a></h3> + + <div class="summary"> + <div class="document"> + + +<p>Gerbolyze is a tool that allows the modification of Gerber PCB artwork with a vector graphics editor like Inkscape. Gerbolyze directly converts between SVG and Gerber, and accurately reproduces details that other tools can not.</p> +</div> + <a href="http://jaseg.de/projects/gerbolyze/">Read more</a> + </div> + <div class="links"> + <a href="https://git.jaseg.de/gerbolyze.git">Sources</a> + <a href="https://github.com/jaseg/gerbolyze/issues">Issues</a> + <a href="https://gerbolyze.gitlab.io/gerbolyze">Docs</a> + <a href="https://pypi.org/project/gerbolyze">PyPI</a> + </div> +</div> + + + <div class="pagination-links"> + <a href="/projects/">See more<span class="arrow-right"></span></a> + </div> + <h2>Blog</h2> + <div class="card"><h3><a href="/blog/telekom-gpon-sfp/">Ubiquiti EdgeRouter on Deutsche Telekom GPON Fiber</a></h3><strong>2022-02-21</strong> + + <div class="summary"> + Disclaimer I provide this guide as a reference for other knowledgeable users without any warranty. Please feel free to use this as a resource but do not hold me responsible if this does not work for you. There is a significant chance that due to an error on my side or due to Telekom changing their setup this guide will not work for you, and you may end up having to pay for an unsuccessful Telekom technician visit. + <a href="http://jaseg.de/blog/telekom-gpon-sfp/">Read more</a> + </div> +</div> + + <div class="card"><h3><a href="/blog/ihsm-worlds-first-diy-hsm/">New Paper on Inertial Hardware Security Modules</a></h3><strong>2021-11-23</strong> + + <div class="summary"> + World's First DIY HSM Last week, Prof. Dr. Björn Scheuermann and I have published our first joint paper on Hardware Security Modules. In our paper, we introduce Inertial Hardware Security Modules (IHSMs), a new way of building high-security HSMs from basic components. I think the technology we demonstrate in our paper might allow some neat applications where some civil organization deploys a service that no one, not even they themselves, can snoop on. + <a href="http://jaseg.de/blog/ihsm-worlds-first-diy-hsm/">Read more</a> + </div> +</div> + + <div class="card"><h3><a href="/blog/kicad-mesh-plugin/">Kicad Mesh Plugin</a></h3><strong>2020-08-18</strong> + + <div class="summary"> + Tamper Detection Meshes Cryptography is at the foundation of our modern, networked world. From email to card payment infrastructure in brick and mortar stores, cryptographic keys secure almost every part of our digital lives againts cybercriminals or curious surveillance capitalists. Without cryptography, many of the things we routinely do in our lives such as paying for groceries with a credit card, messaging a friend on Signal or unlocking a car with its keyfob would not be possible. + <a href="http://jaseg.de/blog/kicad-mesh-plugin/">Read more</a> + </div> +</div> + + <div class="card"><h3><a href="/blog/private-contact-discovery/">Private Contact Discovery</a></h3><strong>2019-06-22</strong> + + <div class="summary"> + Private Contact Discovery Private Contact Discovery (PCD) is the formal name for the problem modern smartphone messenger applications have on installation: Given a user's address book, find out which of their contacts also use the same messenger without the messenger's servers learning anything about the user's address book. The widespread non-private way to do this is to simply upload the user's address book to the app's operator's servers and do an SQL JOIN keyed on the phone number field against the database of registered users. + <a href="http://jaseg.de/blog/private-contact-discovery/">Read more</a> + </div> +</div> + + <div class="card"><h3><a href="/blog/hsm-basics/">Hardware Security Module Basics</a></h3><strong>2019-05-17</strong> + + <div class="summary"> + Hardware Security Modules and Security Research and Cryptography On May 17 2019 I gave a short presentation on the fundamentals of hardware security modules at the weekly seminar of Prof. Mori's security research working group at Waseda University. The motivation for this was that outside of low-level hardware security people and people working in the financial industry HSMs are not thought about that often. In particular most network or systems security people would not consider them an option. + <a href="http://jaseg.de/blog/hsm-basics/">Read more</a> + </div> +</div> + + <div class="card"><h3><a href="/blog/serial-protocols/">How to talk to your microcontroller over serial</a></h3><strong>2018-05-19</strong> + + <div class="summary"> + Scroll to the end for the TL;DR. +In this article I will give an overview on the protocols spoken on serial ports, highlighting common pitfalls. I will summarize some points on how to design a serial protocol that is simple to implement and works reliably even under error conditions. +If you have done low-level microcontroller firmware you will regularly have had to stuff some data up a serial port to another microcontroller or to a computer. + <a href="http://jaseg.de/blog/serial-protocols/">Read more</a> + </div> +</div> + + <div class="card"><h3><a href="/blog/thors-hammer/">Thor's Hammer</a></h3><strong>2018-05-03</strong> + + <div class="summary"> + In case you were having an inferiority complex because your friends' IBM Model M keyboards are so much louder than the shitty rubber dome freebie you got with your pc... Here's the solution: Thor's Hammer, a simple typing cadence enhancer for PS/2 keyboards. +Your browser does not support the HTML5 video tag. A demonstration of the completed project. h264 download / webm download The connects to the keyboard's PS/2 clock line and briefly actuates a large solenoid on each key press. + <a href="http://jaseg.de/blog/thors-hammer/">Read more</a> + </div> +</div> + + <div class="card"><h3><a href="/blog/multichannel-led-driver/">32-Channel LED tape driver</a></h3><strong>2018-05-02</strong> + + <div class="summary"> + Theoretical basics Together, a friend and I outfitted the small staircase at Berlin's Chaos Computer Club with nice, shiny RGB-WW LED tape for ambient lighting. This tape is like regular RGB tape but with an additional warm white channel, which makes for much more natural pastels and whites. There are several variants of RGBW tape. Cheap ones have separate RGB and white LEDs, which is fine for indirect lighting but does not work for direct lighting. + <a href="http://jaseg.de/blog/multichannel-led-driver/">Read more</a> + </div> +</div> + + <div class="card"><h3><a href="/blog/wifi-led-driver/">Wifi Led Driver</a></h3><strong>2018-05-02</strong> + + <div class="summary"> + Project motivation The completed driver board installed in the 3D-printed case. This device can now be connected to 12V and two segments of LED tape that can then be controlled trough Wifi. The ESP8266 module goes on the pin header on the left and was removed for this picture. After the multichannel LED driver was completed, I was just getting used to controlling LEDs at 14-bit resolution. I liked the board we designed in this project, but at 32 channels it was a bit large for most use cases. + <a href="http://jaseg.de/blog/wifi-led-driver/">Read more</a> + </div> +</div> + + <div class="card"><h3><a href="/blog/led-characterization/">LED Characterization</a></h3><strong>2018-05-02</strong> + + <div class="summary"> + Preface Recently, I have been working on a small driver for ambient lighting using 12V LED strips like you can get inexpensively from China. I wanted to be able to just throw one of these somewhere, stick down some LED tape, hook it up to a small transformer and be able to control it through Wifi. When I was writing the firmware, I noticed that when fading between different colors, the colors look all wrong! + <a href="http://jaseg.de/blog/led-characterization/">Read more</a> + </div> +</div> + + + <div class="pagination-links"> + <a href="/blog/">See more<span class="arrow-right"></span></a> + </div> + </main><footer> + Copyright © 2023 Jan Sebastian Götte + / <a href="/about/">About</a> + / <a href="/imprint/">Imprint</a> +</footer> +<script> + if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser + contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to + prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition. + In support of an open web, this website does not function with this DRM. Please install a browser such + as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports + ad blockers.`; + </script> + </body> +</html> diff --git a/index.xml b/index.xml new file mode 100644 index 0000000..96df985 --- /dev/null +++ b/index.xml @@ -0,0 +1,215 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> + <channel> + <title>jaseg.de on Home</title> + <link>http://jaseg.de/</link> + <description>Recent content in jaseg.de on Home</description> + <generator>Hugo -- gohugo.io</generator> + <language>en-us</language> + <copyright>Jan Sebastian Götte</copyright> + <lastBuildDate>Wed, 04 Oct 2023 23:42:00 +0200</lastBuildDate><atom:link href="http://jaseg.de/index.xml" rel="self" type="application/rss+xml" /> + <item> + <title>KiMesh</title> + <link>http://jaseg.de/projects/kimesh/</link> + <pubDate>Wed, 04 Oct 2023 23:42:00 +0200</pubDate> + + <guid>http://jaseg.de/projects/kimesh/</guid> + <description><div class="document"> + + +<p>KiMesh is a KiCad plugin that automatically creates security meshes with two or traces covering an arbitrarily-shaped outline on the board.</p> +</div></description> + </item> + + <item> + <title>Ubiquiti EdgeRouter on Deutsche Telekom GPON Fiber</title> + <link>http://jaseg.de/blog/telekom-gpon-sfp/</link> + <pubDate>Mon, 21 Feb 2022 20:00:00 +0100</pubDate> + + <guid>http://jaseg.de/blog/telekom-gpon-sfp/</guid> + <description>Disclaimer I provide this guide as a reference for other knowledgeable users without any warranty. Please feel free to use this as a resource but do not hold me responsible if this does not work for you. There is a significant chance that due to an error on my side or due to Telekom changing their setup this guide will not work for you, and you may end up having to pay for an unsuccessful Telekom technician visit.</description> + </item> + + <item> + <title>New Paper on Inertial Hardware Security Modules</title> + <link>http://jaseg.de/blog/ihsm-worlds-first-diy-hsm/</link> + <pubDate>Tue, 23 Nov 2021 23:42:20 +0100</pubDate> + + <guid>http://jaseg.de/blog/ihsm-worlds-first-diy-hsm/</guid> + <description>World's First DIY HSM Last week, Prof. Dr. Björn Scheuermann and I have published our first joint paper on Hardware Security Modules. In our paper, we introduce Inertial Hardware Security Modules (IHSMs), a new way of building high-security HSMs from basic components. I think the technology we demonstrate in our paper might allow some neat applications where some civil organization deploys a service that no one, not even they themselves, can snoop on.</description> + </item> + + <item> + <title>Kicad Mesh Plugin</title> + <link>http://jaseg.de/blog/kicad-mesh-plugin/</link> + <pubDate>Tue, 18 Aug 2020 13:15:39 +0200</pubDate> + + <guid>http://jaseg.de/blog/kicad-mesh-plugin/</guid> + <description>Tamper Detection Meshes Cryptography is at the foundation of our modern, networked world. From email to card payment infrastructure in brick and mortar stores, cryptographic keys secure almost every part of our digital lives againts cybercriminals or curious surveillance capitalists. Without cryptography, many of the things we routinely do in our lives such as paying for groceries with a credit card, messaging a friend on Signal or unlocking a car with its keyfob would not be possible.</description> + </item> + + <item> + <title>Private Contact Discovery</title> + <link>http://jaseg.de/blog/private-contact-discovery/</link> + <pubDate>Sat, 22 Jun 2019 10:30:00 +0800</pubDate> + + <guid>http://jaseg.de/blog/private-contact-discovery/</guid> + <description>Private Contact Discovery Private Contact Discovery (PCD) is the formal name for the problem modern smartphone messenger applications have on installation: Given a user's address book, find out which of their contacts also use the same messenger without the messenger's servers learning anything about the user's address book. The widespread non-private way to do this is to simply upload the user's address book to the app's operator's servers and do an SQL JOIN keyed on the phone number field against the database of registered users.</description> + </item> + + <item> + <title>Hardware Security Module Basics</title> + <link>http://jaseg.de/blog/hsm-basics/</link> + <pubDate>Fri, 17 May 2019 15:29:20 +0800</pubDate> + + <guid>http://jaseg.de/blog/hsm-basics/</guid> + <description>Hardware Security Modules and Security Research and Cryptography On May 17 2019 I gave a short presentation on the fundamentals of hardware security modules at the weekly seminar of Prof. Mori's security research working group at Waseda University. The motivation for this was that outside of low-level hardware security people and people working in the financial industry HSMs are not thought about that often. In particular most network or systems security people would not consider them an option.</description> + </item> + + <item> + <title>How to talk to your microcontroller over serial</title> + <link>http://jaseg.de/blog/serial-protocols/</link> + <pubDate>Sat, 19 May 2018 08:09:46 +0200</pubDate> + + <guid>http://jaseg.de/blog/serial-protocols/</guid> + <description>Scroll to the end for the TL;DR. +In this article I will give an overview on the protocols spoken on serial ports, highlighting common pitfalls. I will summarize some points on how to design a serial protocol that is simple to implement and works reliably even under error conditions. +If you have done low-level microcontroller firmware you will regularly have had to stuff some data up a serial port to another microcontroller or to a computer.</description> + </item> + + <item> + <title>Thor's Hammer</title> + <link>http://jaseg.de/blog/thors-hammer/</link> + <pubDate>Thu, 03 May 2018 11:59:37 +0200</pubDate> + + <guid>http://jaseg.de/blog/thors-hammer/</guid> + <description>In case you were having an inferiority complex because your friends' IBM Model M keyboards are so much louder than the shitty rubber dome freebie you got with your pc... Here's the solution: Thor's Hammer, a simple typing cadence enhancer for PS/2 keyboards. +Your browser does not support the HTML5 video tag. A demonstration of the completed project. h264 download / webm download The connects to the keyboard's PS/2 clock line and briefly actuates a large solenoid on each key press.</description> + </item> + + <item> + <title>32-Channel LED tape driver</title> + <link>http://jaseg.de/blog/multichannel-led-driver/</link> + <pubDate>Wed, 02 May 2018 11:31:14 +0200</pubDate> + + <guid>http://jaseg.de/blog/multichannel-led-driver/</guid> + <description>Theoretical basics Together, a friend and I outfitted the small staircase at Berlin's Chaos Computer Club with nice, shiny RGB-WW LED tape for ambient lighting. This tape is like regular RGB tape but with an additional warm white channel, which makes for much more natural pastels and whites. There are several variants of RGBW tape. Cheap ones have separate RGB and white LEDs, which is fine for indirect lighting but does not work for direct lighting.</description> + </item> + + <item> + <title>Wifi Led Driver</title> + <link>http://jaseg.de/blog/wifi-led-driver/</link> + <pubDate>Wed, 02 May 2018 11:31:03 +0200</pubDate> + + <guid>http://jaseg.de/blog/wifi-led-driver/</guid> + <description>Project motivation The completed driver board installed in the 3D-printed case. This device can now be connected to 12V and two segments of LED tape that can then be controlled trough Wifi. The ESP8266 module goes on the pin header on the left and was removed for this picture. After the multichannel LED driver was completed, I was just getting used to controlling LEDs at 14-bit resolution. I liked the board we designed in this project, but at 32 channels it was a bit large for most use cases.</description> + </item> + + <item> + <title>LED Characterization</title> + <link>http://jaseg.de/blog/led-characterization/</link> + <pubDate>Wed, 02 May 2018 11:18:38 +0200</pubDate> + + <guid>http://jaseg.de/blog/led-characterization/</guid> + <description>Preface Recently, I have been working on a small driver for ambient lighting using 12V LED strips like you can get inexpensively from China. I wanted to be able to just throw one of these somewhere, stick down some LED tape, hook it up to a small transformer and be able to control it through Wifi. When I was writing the firmware, I noticed that when fading between different colors, the colors look all wrong!</description> + </item> + + <item> + <title>About jaseg</title> + <link>http://jaseg.de/about/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + + <guid>http://jaseg.de/about/</guid> + <description>About Hej, I'm Jan, or jaseg. At the moment I'm doing a PhD (Dr.-Ing.) at TU Darmstadt in Computer Science, specializing on Hardware Security. This is my personal website where I publish things that I find interesting. +I self-host my code at git.jaseg.de, but I am also on github and on gitlab. I use github for issue tracking for some of my projects such as gerbolyze and python-mpv. I maintain the python-mpv and gerbolyze python packages on PyPI.</description> + </item> + + <item> + <title>Gerbolyze</title> + <link>http://jaseg.de/projects/gerbolyze/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + + <guid>http://jaseg.de/projects/gerbolyze/</guid> + <description><div class="document"> + + +<p>Gerbolyze is a tool that allows the modification of Gerber PCB artwork with a vector graphics editor like Inkscape. Gerbolyze directly converts between SVG and Gerber, and accurately reproduces details that other tools can not.</p> +</div></description> + </item> + + <item> + <title>Gerbonara</title> + <link>http://jaseg.de/projects/gerbonara/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + + <guid>http://jaseg.de/projects/gerbonara/</guid> + <description><div class="document"> + + +<p>Gerbonara is a user-friendly, powerful tool for reading, writing, modification and rendering of Gerber PCB artwork from the command line or from Python code. Gerbonara supports the Gerber dialects of all industry-standard EDA tools.</p> +</div></description> + </item> + + <item> + <title>Impressum</title> + <link>http://jaseg.de/imprint/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + + <guid>http://jaseg.de/imprint/</guid> + <description>Impressum Sebastian Götte c/o Praxis Dr. Götte Krankenhausstr. 1a 54634 Bitburg imprint@jaseg.net Inhaltlich Verantwortlicher gem. § 55 II RStV: Sebastian Götte (Anschrift s.o.) Lizenz dieser Seite Dieses Werk ist lizenziert unter einer Creative Commons Namensnennung - Nicht-kommerziell - Weitergabe unter gleichen Bedingungen 4.0 International Lizenz (CC-BY-NC-SA) . Haftungsbeschränkung für Inhalte der Website Gemäß § 7 Abs. 1 TMG ist der Verantwortliche der Website i. S. v. § 5 TMG für eigene Informationen, die er zur Nutzung bereithält, nach den allgemeinen Gesetzen verantwortlich.</description> + </item> + + <item> + <title>lolcat-c</title> + <link>http://jaseg.de/projects/lolcat-c/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + + <guid>http://jaseg.de/projects/lolcat-c/</guid> + <description><div class="document"> + + +<p>lolcat-c is a small, high-performance re-implementation of the <a class="reference external" href="https://github.com/busyloop/lolcat">lolcat</a> rainbow cat utility. lolcat-c is meant as a lolcat that you can actually use in production. It is fast, not slowing down whatever you pipe through it, and it robustly handles real-world terminal output including escape sequences.</p> +</div></description> + </item> + + <item> + <title>python-mpv</title> + <link>http://jaseg.de/projects/python-mpv/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + + <guid>http://jaseg.de/projects/python-mpv/</guid> + <description><div class="document"> + + +<p>python-mpv is a small, ctypes-based Python library wrapping the libmpv media player library. Despite its small size and simple API, python-mpv allows advanced control over libmpv and beyond simple remote control of mpv can be used to embed mpv in OpenGL, Qt, and GTK-based Python applications.</p> +</div></description> + </item> + + <item> + <title>svg-flatten</title> + <link>http://jaseg.de/projects/svg-flatten/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + + <guid>http://jaseg.de/projects/svg-flatten/</guid> + <description><div class="document"> + + +<p>svg-flatten is a command-line utility that performs vector occlusion and clipping on SVG files, producing a flattened SVG file without overlapping elements, without changing what the file looks like. svg-flatten is used as a part of gerbolyze.</p> +</div></description> + </item> + + <item> + <title>wsdiff</title> + <link>http://jaseg.de/projects/wsdiff/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + + <guid>http://jaseg.de/projects/wsdiff/</guid> + <description><div class="document"> + + +<p>wsdiff is a command-line utility that produces self-contained, syntax-highlighted, HTML-formatted diffs that support both unified and side-by-side diffs from a single source file using nothing but CSS magic.</p> +</div></description> + </item> + + </channel> +</rss> diff --git a/posts/index.html b/posts/index.html new file mode 100644 index 0000000..5d4a7d1 --- /dev/null +++ b/posts/index.html @@ -0,0 +1,49 @@ +<!DOCTYPE html> +<html><head> + <meta charset="utf-8"> + <title>Posts | Home</title> + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="mobile-web-app-capable" content="yes"> + <meta name="color-scheme" content="dark light"> + <link rel="stylesheet" href="/style.css"> +</head> +<body><nav> + <div class="internal"> + + <a href="/" title="Home">Home</a> + <a href="/blog/" title="Blog">Blog</a> + <a href="/projects/" title="Projects">Projects</a> + <a href="/about/" title="About">About</a> + </div> + <div class="external"> + <a href="https://git.jaseg.de/" title="cgit">cgit</a> + <a href="https://github.com/jaseg" title="Github">Github</a> + <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a> + <a href="https://chaos.social/jaseg" title="Mastodon">Mastodon</a> + </span> +</nav> + + <header> + <h1>Posts</h1> +<ul class="breadcrumbs"> + <li><a href="/">jaseg.de</a></li><li><a href="/posts/">Posts</a></li> +</ul> + + </header> + <main class="cards"> + </main><footer> + Copyright © 2023 Jan Sebastian Götte + / <a href="/about/">About</a> + / <a href="/imprint/">Imprint</a> +</footer> +<script> + if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser + contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to + prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition. + In support of an open web, this website does not function with this DRM. Please install a browser such + as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports + ad blockers.`; + </script> + </body> +</html> diff --git a/posts/index.xml b/posts/index.xml new file mode 100644 index 0000000..68e5aab --- /dev/null +++ b/posts/index.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> + <channel> + <title>Posts on Home</title> + <link>http://jaseg.de/posts/</link> + <description>Recent content in Posts on Home</description> + <generator>Hugo -- gohugo.io</generator> + <language>en-us</language> + <copyright>Jan Sebastian Götte</copyright><atom:link href="http://jaseg.de/posts/index.xml" rel="self" type="application/rss+xml" /> + </channel> +</rss> diff --git a/projects/gerbolyze/index.html b/projects/gerbolyze/index.html new file mode 100644 index 0000000..61be168 --- /dev/null +++ b/projects/gerbolyze/index.html @@ -0,0 +1,650 @@ +<!DOCTYPE html> +<html><head> + <meta charset="utf-8"> + <title>Gerbolyze | Home</title> + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="mobile-web-app-capable" content="yes"> + <meta name="color-scheme" content="dark light"> + <link rel="stylesheet" href="/style.css"> +</head> +<body><nav> + <div class="internal"> + + <a href="/" title="Home">Home</a> + <a href="/blog/" title="Blog">Blog</a> + <a href="/projects/" title="Projects">Projects</a> + <a href="/about/" title="About">About</a> + </div> + <div class="external"> + <a href="https://git.jaseg.de/" title="cgit">cgit</a> + <a href="https://github.com/jaseg" title="Github">Github</a> + <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a> + <a href="https://chaos.social/jaseg" title="Mastodon">Mastodon</a> + </span> +</nav> + + <header> + <h1>Gerbolyze</h1> +<ul class="breadcrumbs"> + <li><a href="/">jaseg.de</a></li> + <li><a href="/projects/">Projects</a></li><li><a href="/projects/gerbolyze/">Gerbolyze</a></li> +</ul> + + </header> + <main> + <div class="links"> + <a href="https://git.jaseg.de/gerbolyze.git">Sources</a> + <a href="https://github.com/jaseg/gerbolyze/issues">Issues</a> + <a href="https://gerbolyze.gitlab.io/gerbolyze">Docs</a> + <a href="https://pypi.org/project/gerbolyze">PyPI</a> + </div> + <div class="document"> + + +<p>Gerbolyze renders SVG vector and PNG/JPG raster images into existing gerber PCB manufacturing files. +Vector data from SVG files is rendered losslessly <em>without</em> an intermediate rasterization/revectorization step. +Still, gerbolyze supports (almost) the full SVG 1.1 spec including complex, self-intersecting paths with holes, +patterns, dashes and transformations.</p> +<p>Raster images can either be vectorized through contour tracing (like gerbolyze v1.0 did) or they can be embedded using +high-resolution grayscale emulation while (mostly) guaranteeing trace/space design rules.</p> +<p>Try gerbolyze online at <a class="reference external" href="https://dyna.kokoroyukuma.de/gerboweb">https://dyna.kokoroyukuma.de/gerboweb</a></p> +<div class="figure"> +<img alt="pics/pcbway_sample_02_small.jpg" src="pics/pcbway_sample_02_small.jpg" style="width: 800px;" /> +<p class="caption">Drawing by <a class="reference external" href="https://twitter.com/fluffy2038/status/1317231121269104640">トーコ Toko</a> converted using Gerbolyze and printed at PCBWay.</p> +</div> +<p>Tooling for PCB art is quite limited in both open source and closed source ecosystems. Something as simple as putting a +pretty picture on a PCB can be an extremely tedious task. Depending on the PCB tool used, various arcane incantations +may be necessary and even modestly complex images will slow down most PCB tools to a crawl.</p> +<p>Gerbolyze solves this problem in a toolchain-agnostic way by directly vectorizing SVG vector and PNG or JPG bitmap files +onto existing gerber layers. Gerbolyze processes any spec-compliant SVG and "gerbolyzes" SVG vector data into a Gerber +spec-compliant form. Gerbolyze has been tested against both the leading open-source KiCAD toolchain and the +industry-standard Altium Designer. Gerbolyze is written with performance in mind and will happily vectorize tens of +thousands of primitives, generating tens of megabytes of gerber code without crapping itself. With gerbolyze you can +finally be confident that your PCB fab's toolchain will fall over before yours does if you overdo it with the high-poly +anime silkscreen.</p> +<p>Gerbolyze is based on <a class="reference external" href="https://gitlab.com/gerbolyze/gerbonara">gerbonara</a>.</p> +<img alt="pics/process-overview.png" src="pics/process-overview.png" style="width: 800px;" /> +<div class="contents topic" id="contents"> +<p class="topic-title">Contents</p> +<ul class="simple"> +<li><a class="reference internal" href="#tl-dr-produce-high-quality-artistic-pcbs-in-three-easy-steps" id="toc-entry-1">Tl;dr: Produce high-quality artistic PCBs in three easy steps!</a></li> +<li><a class="reference internal" href="#quick-start-installation-any-platform" id="toc-entry-2">Quick Start Installation (Any Platform)</a></li> +<li><a class="reference internal" href="#speeding-up-gerbolyze-using-natively-built-binaries" id="toc-entry-3">Speeding up gerbolyze using natively-built binaries</a><ul> +<li><a class="reference internal" href="#build-from-source-any-distro" id="toc-entry-4">Build from source (any distro)</a></li> +</ul> +</li> +<li><a class="reference internal" href="#features" id="toc-entry-5">Features</a></li> +<li><a class="reference internal" href="#algorithm-overview" id="toc-entry-6">Algorithm Overview</a></li> +<li><a class="reference internal" href="#web-interface" id="toc-entry-7">Web interface</a></li> +<li><a class="reference internal" href="#command-line-usage" id="toc-entry-8">Command-line usage</a><ul> +<li><a class="reference internal" href="#gerbolyze-template" id="toc-entry-9"><tt class="docutils literal">gerbolyze template</tt></a><ul> +<li><a class="reference internal" href="#options" id="toc-entry-10">Options:</a></li> +</ul> +</li> +<li><a class="reference internal" href="#gerbolyze-paste" id="toc-entry-11"><tt class="docutils literal">gerbolyze paste</tt></a><ul> +<li><a class="reference internal" href="#options-1" id="toc-entry-12">Options:</a></li> +<li><a class="reference internal" href="#outline-layers-1" id="toc-entry-13">Outline layers</a></li> +<li><a class="reference internal" href="#subtraction-scripts" id="toc-entry-14">Subtraction scripts</a></li> +</ul> +</li> +<li><a class="reference internal" href="#svg-flatten-1" id="toc-entry-15"><tt class="docutils literal"><span class="pre">svg-flatten</span></tt></a><ul> +<li><a class="reference internal" href="#options-2" id="toc-entry-16">Options:</a></li> +</ul> +</li> +</ul> +</li> +<li><a class="reference internal" href="#gerbolyze-image-vectorization" id="toc-entry-17">Gerbolyze image vectorization</a><ul> +<li><a class="reference internal" href="#vectorizer-poisson-disc-the-default" id="toc-entry-18"><tt class="docutils literal"><span class="pre">--vectorizer</span> <span class="pre">poisson-disc</span></tt> (the default)</a></li> +<li><a class="reference internal" href="#vectorizer-hex-grid" id="toc-entry-19"><tt class="docutils literal"><span class="pre">--vectorizer</span> <span class="pre">hex-grid</span></tt></a></li> +<li><a class="reference internal" href="#vectorizer-square-grid" id="toc-entry-20"><tt class="docutils literal"><span class="pre">--vectorizer</span> <span class="pre">square-grid</span></tt></a></li> +<li><a class="reference internal" href="#vectorizer-binary-contours" id="toc-entry-21"><tt class="docutils literal"><span class="pre">--vectorizer</span> <span class="pre">binary-contours</span></tt></a></li> +</ul> +</li> +<li><a class="reference internal" href="#gimp-halftone-preprocessing-guide" id="toc-entry-22">GIMP halftone preprocessing guide</a><ul> +<li><a class="reference internal" href="#import-your-desired-artwork" id="toc-entry-23">1 Import your desired artwork</a></li> +<li><a class="reference internal" href="#convert-the-image-to-grayscale" id="toc-entry-24">2 Convert the image to grayscale</a></li> +<li><a class="reference internal" href="#fine-tune-the-image-s-contrast" id="toc-entry-25">3 Fine-tune the image's contrast</a></li> +<li><a class="reference internal" href="#retouch-details" id="toc-entry-26">4 Retouch details</a></li> +<li><a class="reference internal" href="#run-the-newsprint-filter" id="toc-entry-27">5 Run the newsprint filter</a></li> +<li><a class="reference internal" href="#export-the-image-for-use-with-gerbolyze-vectorize" id="toc-entry-28">6 Export the image for use with <tt class="docutils literal">gerbolyze vectorize</tt></a></li> +</ul> +</li> +<li><a class="reference internal" href="#manufacturing-considerations" id="toc-entry-29">Manufacturing Considerations</a></li> +<li><a class="reference internal" href="#limitations" id="toc-entry-30">Limitations</a><ul> +<li><a class="reference internal" href="#svg-raster-features" id="toc-entry-31">SVG raster features</a></li> +<li><a class="reference internal" href="#gerber-pass-through" id="toc-entry-32">Gerber pass-through</a></li> +<li><a class="reference internal" href="#trace-space-design-rule-adherence" id="toc-entry-33">Trace/Space design rule adherence</a></li> +</ul> +</li> +<li><a class="reference internal" href="#gallery" id="toc-entry-34">Gallery</a></li> +<li><a class="reference internal" href="#licensing" id="toc-entry-35">Licensing</a></li> +</ul> +</div> +<div class="section" id="tl-dr-produce-high-quality-artistic-pcbs-in-three-easy-steps"> +<h2><a class="toc-backref" href="#toc-entry-1">Tl;dr: Produce high-quality artistic PCBs in three easy steps!</a></h2> +<p>Gerbolyze works in three steps.</p> +<ol class="arabic"> +<li><p class="first">Generate a scale-accurate template of the finished PCB from your CAD tool's gerber output:</p> +<pre class="code literal-block"> +<span class="lineno"></span><span class="line">$ gerbolyze template --top template_top.svg [--bottom template_bottom.svg] my_gerber_dir +</span></pre> +</li> +<li><p class="first">Load the resulting template image <a class="reference external" href="https://inkscape.org/">Inkscape</a> or another SVG editing program. Put your artwork on the appropriate SVG +layer. Dark colors become filled gerber primitives, bright colors become unfilled primitives. You can directly put +raster images (PNG/JPG) into this SVG as well, just position and scale them like everything else. SVG clips work for +images, too. Masks are not supported.</p> +</li> +<li><p class="first">Vectorize the edited SVG template image drectly into the PCB's gerber files:</p> +<pre class="code literal-block"> +<span class="lineno"></span><span class="line">$ gerbolyze paste --top template_top_edited.svg [--bottom ...] my_gerber_dir output_gerber_dir +</span></pre> +</li> +</ol> +</div> +<div class="section" id="quick-start-installation-any-platform"> +<h2><a class="toc-backref" href="#toc-entry-2">Quick Start Installation (Any Platform)</a></h2> +<pre class="code shell literal-block"> +<span class="lineno"></span><span class="line">python<span class="w"> </span>-m<span class="w"> </span>pip<span class="w"> </span>install<span class="w"> </span>--user<span class="w"> </span>gerbolyze +</span></pre> +<p>To uninstall, run</p> +<pre class="code shell literal-block"> +<span class="lineno"></span><span class="line">python<span class="w"> </span>-m<span class="w"> </span>pip<span class="w"> </span>uninstall<span class="w"> </span>gerbolyze<span class="w"> </span>gerbonara<span class="w"> </span>resvg-wasi<span class="w"> </span>svg-flatten-wasi +</span></pre> +<p>To update, run</p> +<pre class="code shell literal-block"> +<span class="lineno"></span><span class="line">python<span class="w"> </span>-m<span class="w"> </span>pip<span class="w"> </span>install<span class="w"> </span>--user<span class="w"> </span>--upgrade<span class="w"> </span>--upgrade-strategy<span class="w"> </span>eager<span class="w"> </span>gerbolyze +</span></pre> +</div> +<div class="section" id="speeding-up-gerbolyze-using-natively-built-binaries"> +<h2><a class="toc-backref" href="#toc-entry-3">Speeding up gerbolyze using natively-built binaries</a></h2> +<p>This will install gerbolyze's binary dependency resvg and gerbolyze's svg-flatten utility as pre-built cross-platform +WASM binaries. When you first run gerbolyze, it will take some time (~30s) to link these binaries for your system. The +output is cached, so any future run is going to be fast.</p> +<p>WASM is slower than natively-built binaries. To speed up gerbolyze, you can natively build its two binary dependencies:</p> +<ol class="arabic simple"> +<li>Install resvg natively using rust's cargo package manager: <tt class="docutils literal">cargo install resvg</tt></li> +<li>Install gerbolyze's svg-flatten utility natively. You can get pre-built binaries from gerbolyze's gitlab CI jobs <a class="reference external" href="https://gitlab.com/gerbolyze/gerbolyze/-/pipelines?scope=tags&page=1">at +this link</a> by clicking the three dots on the +right next to the version you want. These pre-built binaries should work on any x86_64 linux since they are +statically linked. You can also build svg-flatten yourself by running <tt class="docutils literal">make</tt> inside the <tt class="docutils literal"><span class="pre">svg-flatten</span></tt> folder from +a gerbolyze checkout.</li> +</ol> +<p>Gerbolyze will pick up these binaries when installed in your <tt class="docutils literal">$PATH</tt>. resvg is also picked up when it is installed by +cargo in your home's <tt class="docutils literal"><span class="pre">~/.cargo</span></tt>, even if it's not in your <tt class="docutils literal">$PATH</tt>. You can override the resvg, usvg or svg-flatten +binary that gerbolyze uses by giving it the absoulute path to a binary in the <tt class="docutils literal">$RESVG</tt>, <tt class="docutils literal">$USVG</tt> and <tt class="docutils literal">$SVG_FLATTEN</tt> +environment variables.</p> +<div class="section" id="build-from-source-any-distro"> +<h3><a class="toc-backref" href="#toc-entry-4">Build from source (any distro)</a></h3> +<pre class="code sh literal-block"> +<span class="lineno"></span><span class="line">git<span class="w"> </span>clone<span class="w"> </span>--recurse-submodules<span class="w"> </span>https://git.jaseg.de/gerbolyze.git<span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="nb">cd</span><span class="w"> </span>gerbolyze<span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span>python3<span class="w"> </span>-m<span class="w"> </span>venv<span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="nb">source</span><span class="w"> </span>venv/bin/activate<span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span>python3<span class="w"> </span>setup.py<span class="w"> </span>install +</span></pre> +</div> +</div> +<div class="section" id="features"> +<h2><a class="toc-backref" href="#toc-entry-5">Features</a></h2> +<p>Input on the left, output on the right.</p> +<img alt="pics/test_svg_readme_composited.png" src="pics/test_svg_readme_composited.png" style="width: 800px;" /> +<ul class="simple"> +<li>Almost full SVG 1.1 static spec coverage (!)<ul> +<li>Paths with beziers, self-intersections and holes</li> +<li>Strokes, even with dashes and markers</li> +<li>Pattern fills and strokes</li> +<li>Transformations and nested groups</li> +<li>Proper text rendering with support for complex text layout (e.g. Arabic)</li> +<li><image> elements via either built-in vectorizer or built-in halftone processor</li> +<li>(some) CSS</li> +</ul> +</li> +<li>Writes Gerber, SVG or KiCAD S-Expression (<tt class="docutils literal">.kicad_mod</tt>) formats</li> +<li>Can export from top/bottom SVGs to a whole gerber layer stack at once with filename autodetection</li> +<li>Can export SVGs to <tt class="docutils literal">.kicad_mod</tt> files like svg2mod (but with full SVG support)</li> +<li>Beziers flattening with configurable tolerance using actual math!</li> +<li>Polygon intersection removal</li> +<li>Polygon hole removal (!)</li> +<li>Optionally vector-compositing of output: convert black/white/transparent image to black/transparent image</li> +<li>Renders SVG templates from input gerbers for accurate and easy scaling and positioning of artwork</li> +<li>layer masking with offset (e.g. all silk within 1mm of soldermask)</li> +<li>Can read gerbers from zip files</li> +<li>Limited SVG support for board outline layers (no fill/region support)</li> +<li>Dashed lines supported on board outline layers</li> +</ul> +<p>Gerbolyze is the end-to-end "paste this svg into these gerbers" command that handles all layers on both board sides at +once. The heavy-duty computer geometry logic of gerbolyze is handled by the svg-flatten utility (<tt class="docutils literal"><span class="pre">svg-flatten</span></tt> +directory). svg-flatten reads an SVG file and renders it into a variety of output formats. svg-flatten can be used like +a variant of the popular svg2mod that supports all of SVG and handles arbitrary input <tt class="docutils literal"><path></tt> elements.</p> +</div> +<div class="section" id="algorithm-overview"> +<h2><a class="toc-backref" href="#toc-entry-6">Algorithm Overview</a></h2> +<p>This is the algorithm gerbolyze uses to process a stack of gerbers.</p> +<ul class="simple"> +<li>Map input files to semantic layers by their filenames</li> +<li>For each layer:<ul> +<li>load input gerber</li> +<li>Pass mask layers through <tt class="docutils literal">gerbv</tt> for conversion to SVG</li> +<li>Pass mask layers SVG through <tt class="docutils literal"><span class="pre">svg-flatten</span> <span class="pre">--dilate</span></tt></li> +<li>Pass input SVG through <tt class="docutils literal"><span class="pre">svg-flatten</span> <span class="pre">--only-groups</span> [layer]</tt></li> +<li>Overlay input gerber, mask and input svg</li> +<li>Write result to output gerber</li> +</ul> +</li> +</ul> +<p>This is the algorithm svg-flatten uses to process an SVG.</p> +<ul class="simple"> +<li>pass input SVG through <a class="reference external" href="https://github.com/RazrFalcon/resvg">usvg</a></li> +<li>iterate depth-first through resulting SVG.<ul> +<li>for groups: apply transforms and clip and recurse</li> +<li>for images: Vectorize using selected vectorizer</li> +<li>for paths:<ul> +<li>flatten path using Cairo</li> +<li>remove self-intersections using Clipper</li> +<li>if stroke is set: process dash, then offset using Clipper</li> +<li>apply pattern fills</li> +<li>clip to clip-path</li> +<li>remove holes using Clipper</li> +</ul> +</li> +</ul> +</li> +<li>for KiCAD S-Expression export: vector-composite results using CavalierContours: subtract each clear output primitive +from all previous dark output primitives</li> +</ul> +</div> +<div class="section" id="web-interface"> +<h2><a class="toc-backref" href="#toc-entry-7">Web interface</a></h2> +<p>You can try gerbolyze online at <a class="reference external" href="https://dyna.kokoroyukuma.de/gerboweb">https://dyna.kokoroyukuma.de/gerboweb</a></p> +<p>The web interface does not expose all of gerbolyze's bells and whistles, but it allows you to simply paste a single SVG +file on a board to try out gerbolyze. Upload your design on the web interface, then download the template for either the +top or bottom side, and put your artwork on the appropriate layer of that template using <a class="reference external" href="https://inkscape.org/">Inkscape</a>. Finally, upload the +modified template and let gerbolyze process your design.</p> +</div> +<div class="section" id="command-line-usage"> +<h2><a class="toc-backref" href="#toc-entry-8">Command-line usage</a></h2> +<p id="command-line-usage-1">Generate SVG template from Gerber files:</p> +<pre class="code shell literal-block"> +<span class="lineno"></span><span class="line">gerbolyze<span class="w"> </span>template<span class="w"> </span><span class="o">[</span>options<span class="o">]</span><span class="w"> </span><span class="o">[</span>--top<span class="p">|</span>--bottom<span class="o">]</span><span class="w"> </span>input_dir_or.zip<span class="w"> </span>output.svg +</span></pre> +<p>Render design from an SVG made with the template above into a set of gerber files:</p> +<pre class="code shell literal-block"> +<span class="lineno"></span><span class="line">gerbolyze<span class="w"> </span>paste<span class="w"> </span><span class="o">[</span>options<span class="o">]</span><span class="w"> </span>artwork.svg<span class="w"> </span>input_dir_or.zip<span class="w"> </span>output_dir_or.zip +</span></pre> +<p>Use svg-flatten to convert an SVG file into Gerber or flattened SVG:</p> +<pre class="code shell literal-block"> +<span class="lineno"></span><span class="line">svg-flatten<span class="w"> </span><span class="o">[</span>options<span class="o">]</span><span class="w"> </span>--format<span class="w"> </span><span class="o">[</span>gerber<span class="p">|</span>svg<span class="o">]</span><span class="w"> </span><span class="o">[</span>input_file.svg<span class="o">]</span><span class="w"> </span><span class="o">[</span>output_file<span class="o">]</span> +</span></pre> +<p>Use svg-flatten to convert an SVG file into the given layer of a KiCAD S-Expression (<tt class="docutils literal">.kicad_mod</tt>) file:</p> +<pre class="code shell literal-block"> +<span class="lineno"></span><span class="line">svg-flatten<span class="w"> </span><span class="o">[</span>options<span class="o">]</span><span class="w"> </span>--format<span class="w"> </span>kicad<span class="w"> </span>--sexp-layer<span class="w"> </span>F.SilkS<span class="w"> </span>--sexp-mod-name<span class="w"> </span>My_Module<span class="w"> </span><span class="o">[</span>input_file.svg<span class="o">]</span><span class="w"> </span><span class="o">[</span>output_file<span class="o">]</span> +</span></pre> +<p>Use svg-flatten to convert an SVG file into a <tt class="docutils literal">.kicad_mod</tt> with SVG layers fed into separate KiCAD layers based on +their IDs like the popular <tt class="docutils literal">svg2mod</tt> is doing:</p> +<dl class="docutils"> +<dt>Note:</dt> +<dd><p class="first">Right now, the input SVG's layers must have <em>ids</em> that match up KiCAD's s-exp layer names. Note that when you name +a layer in Inkscape that only sets a <tt class="docutils literal">name</tt> attribute, but does not change the ID. In order to change the ID in +Inkscape, you have to use Inkscape's "object properties" context menu function.</p> +<p>Also note that svg-flatten expects the layer names KiCAD uses in their S-Expression format. These are <em>different</em> to +the layer names KiCAD exposes in the UI (even though most of them match up!).</p> +<p class="last">For your convenience, there is an SVG template with all the right layer names and IDs located next to this README.</p> +</dd> +</dl> +<pre class="code shell literal-block"> +<span class="lineno"></span><span class="line">svg-flatten<span class="w"> </span><span class="o">[</span>options<span class="o">]</span><span class="w"> </span>--format<span class="w"> </span>kicad<span class="w"> </span>--sexp-mod-name<span class="w"> </span>My_Module<span class="w"> </span><span class="o">[</span>input_file.svg<span class="o">]</span><span class="w"> </span><span class="o">[</span>output_file<span class="o">]</span> +</span></pre> +<div class="section" id="gerbolyze-template"> +<h3><a class="toc-backref" href="#toc-entry-9"><tt class="docutils literal">gerbolyze template</tt></a></h3> +<p>Usage: <tt class="docutils literal">gerbolyze template [OPTIONS] INPUT</tt></p> +<p>Generate SVG template for gerbolyze paste from gerber files.</p> +<p>INPUT may be a gerber file, directory of gerber files or zip file with gerber files. The output file contains a preview +image of the input gerbers to allow you to position your artwork, as well as prepared Inkscape layers corresponding to +each gerber layer. Simply place your artwork in this SVG template using Inkscape. Starting in v3.0, gerbolyze +automatically keeps track of which board side (top or bottom) is contained in an SVG template.</p> +<div class="section" id="options"> +<h4><a class="toc-backref" href="#toc-entry-10">Options:</a></h4> +<dl class="docutils"> +<dt><tt class="docutils literal"><span class="pre">--top</span> | <span class="pre">--bottom</span></tt></dt> +<dd>Output top or bottom side template. This affects both the preview image and the prepared Inkscape layers.</dd> +<dt><tt class="docutils literal"><span class="pre">--vector</span> | <span class="pre">--raster</span></tt></dt> +<dd>Embed preview renders into output file as SVG vector graphics instead of rendering them to PNG bitmaps. The +resulting preview may slow down your SVG editor.</dd> +<dt><tt class="docutils literal"><span class="pre">--raster-dpi</span> FLOAT</tt></dt> +<dd>DPI for rastering preview</dd> +<dt><tt class="docutils literal"><span class="pre">--bbox</span> TEXT</tt></dt> +<dd>Output file bounding box. Format: "w,h" to force [w] mm by [h] mm output canvas OR "x,y,w,h" to force [w] mm by [h] +mm output canvas with its bottom left corner at the given input gerber coördinates.</dd> +</dl> +</div> +</div> +<div class="section" id="gerbolyze-paste"> +<h3><a class="toc-backref" href="#toc-entry-11"><tt class="docutils literal">gerbolyze paste</tt></a></h3> +<p>(see <a class="reference internal" href="#vectorization">below</a>)</p> +<p>Usage: <tt class="docutils literal">gerbolyze paste [OPTIONS] INPUT_GERBERS OVERLAY_SVG OUTPUT_GERBERS</tt></p> +<p>Render vector data and raster images from SVG file into gerbers. The SVG input file can be generated using <tt class="docutils literal">gerbolyze +template</tt> and contains the name and board side of each layer. Note that for board outline layers, handling slightly +differs from other layers as PCB fabs do not support filled Gerber regions on these layers.</p> +<div class="section" id="options-1"> +<h4><a class="toc-backref" href="#toc-entry-12">Options:</a></h4> +<dl class="docutils"> +<dt><tt class="docutils literal"><span class="pre">--bbox</span> TEXT</tt></dt> +<dd>Output file bounding box. Format: "w,h" to force [w] mm by [h] mm output canvas OR "x,y,w,h" to force [w] mm by [h] +mm output canvas with its bottom left corner at the given input gerber coördinates. This <strong>must match the ``--bbox`` value given to +template</strong>!</dd> +<dt><tt class="docutils literal"><span class="pre">--subtract</span> TEXT</tt></dt> +<dd>Use user subtraction script from argument (see <a class="reference internal" href="#subtraction-script">below</a>)</dd> +<dt><tt class="docutils literal"><span class="pre">--no-subtract</span></tt></dt> +<dd>Disable subtraction (see <a class="reference internal" href="#subtraction-script">below</a>)</dd> +<dt><tt class="docutils literal"><span class="pre">--dilate</span> FLOAT</tt></dt> +<dd>Default dilation for subtraction operations in mm (see <a class="reference internal" href="#subtraction-script">below</a>)</dd> +<dt><tt class="docutils literal"><span class="pre">--trace-space</span> FLOAT</tt></dt> +<dd>Passed through to svg-flatten, see <a class="reference internal" href="#svg-flatten">below</a>.</dd> +<dt><tt class="docutils literal"><span class="pre">--vectorizer</span> TEXT</tt></dt> +<dd>Passed through to svg-flatten, see <a class="reference internal" href="#svg-flatten">its description below</a>. Also have a look at <a class="reference internal" href="#vectorization">the examples below</a>.</dd> +<dt><tt class="docutils literal"><span class="pre">--vectorizer-map</span> TEXT</tt></dt> +<dd>Passed through to svg-flatten, see <a class="reference internal" href="#svg-flatten">below</a>.</dd> +<dt><tt class="docutils literal"><span class="pre">--exclude-groups</span> TEXT</tt></dt> +<dd>Passed through to svg-flatten, see <a class="reference internal" href="#svg-flatten">below</a>.</dd> +</dl> +</div> +<div class="section" id="outline-layers-1"> +<span id="outline-layers"></span><h4><a class="toc-backref" href="#toc-entry-13">Outline layers</a></h4> +<p>Outline layers require special handling since PCB fabs do not support filled G36/G37 polygons on these layers. The main +difference between normal layers and outline layers is how strokes are handled. On outline layers, strokes are +translated to normal Gerber draw commands (D01, D02 etc.) with an aperture set to the stroke's width instead of tracing +them to G36/G37 filled regions. This means that on outline layers, SVG end caps and line join types do not work: All +lines are redered with round joins and end caps.</p> +<p>One exception from this are patterns, which work as expected for both fills and strokes with full support for joins and +end caps.</p> +<p>Dashed strokes are supported on outline layers and can be used to make easy mouse bites.</p> +</div> +<div class="section" id="subtraction-scripts"> +<span id="subtraction-script"></span><h4><a class="toc-backref" href="#toc-entry-14">Subtraction scripts</a></h4> +<img alt="pics/subtract_example.png" src="pics/subtract_example.png" style="width: 800px;" /> +<p>Subtraction scripts tell <tt class="docutils literal">gerbolyze paste</tt> to remove an area around certain input layers to from an overlay layer. +When a input layer is given in the subtraction script, gerbolyze will dilate (extend outwards) everything on this input +layer and remove it from the target overlay layer. By default, Gerbolyze subtracts the mask layer from the silk layer to +make sure there are no silk primitives that overlap bare copper, and subtracts each input layer from its corresponding +overlay to make sure the two do not overlap. In the picture above you can see both at work: The overlay contains +halftone primitives all over the place. The subtraction script has cut out an area around all pads (mask layer) and all +existing silkscreen. You can turn off this behavior by passing <tt class="docutils literal"><span class="pre">--no-subtract</span></tt> or pass your own "script".</p> +<p>The syntax of these scripts is:</p> +<pre class="code literal-block"> +<span class="lineno"></span><span class="line">{target layer} -= {source layer} {dilation} [; ...] +</span></pre> +<p>The target layer must be <tt class="docutils literal"><span class="pre">out.{layer</span> name}</tt> and the source layer <tt class="docutils literal"><span class="pre">in.{layer</span> name}</tt>. The layer names are gerbolyze's +internal layer names, i.e.: <tt class="docutils literal">paste, silk, mask, copper, outline, drill</tt></p> +<p>The dilation value is optional, but can be a float with a leading <tt class="docutils literal">+</tt> or <tt class="docutils literal">-</tt>. If given, before subtraction the +source layer's features will be extended by that many mm. If not given, the dilation defaults to the value given by +<tt class="docutils literal"><span class="pre">--dilate</span></tt> if given or 0.1 mm otherwise. To disable dilation, simply pass <tt class="docutils literal">+0</tt> here.</p> +<p>Multiple commands can be separated by semicolons <tt class="docutils literal">;</tt> or line breaks.</p> +<p>The default subtraction script is:</p> +<pre class="code literal-block"> +<span class="lineno"></span><span class="line">out.silk -= in.mask</span> +<span class="lineno"></span><span class="line">out.silk -= in.silk+0.5</span> +<span class="lineno"></span><span class="line">out.mask -= in.mask+0.5</span> +<span class="lineno"></span><span class="line">out.copper -= in.copper+0.5 +</span></pre> +</div> +</div> +<div class="section" id="svg-flatten-1"> +<span id="svg-flatten"></span><h3><a class="toc-backref" href="#toc-entry-15"><tt class="docutils literal"><span class="pre">svg-flatten</span></tt></a></h3> +<p>Usage: <tt class="docutils literal"><span class="pre">svg-flatten</span> <span class="pre">[OPTIONS]...</span> [INPUT_FILE] [OUTPUT_FILE]</tt></p> +<p>Specify <tt class="docutils literal">-</tt> for stdin/stdout.</p> +<div class="section" id="options-2"> +<h4><a class="toc-backref" href="#toc-entry-16">Options:</a></h4> +<dl class="docutils"> +<dt><tt class="docutils literal"><span class="pre">-h,</span> <span class="pre">--help</span></tt></dt> +<dd>Print help and exit</dd> +<dt><tt class="docutils literal"><span class="pre">-v,</span> <span class="pre">--version</span></tt></dt> +<dd>Print version and exit</dd> +<dt><tt class="docutils literal"><span class="pre">-o,</span> <span class="pre">--format</span></tt></dt> +<dd>Output format. Supported: gerber, gerber-outline (for board outline layers), svg, s-exp (KiCAD S-Expression)</dd> +<dt><tt class="docutils literal"><span class="pre">-p,</span> <span class="pre">--precision</span></tt></dt> +<dd>Number of decimal places use for exported coordinates (gerber: 1-9, SVG: >=0). Note that not all gerber viewers are +happy with too many digits. 5 or 6 is a reasonable choice.</dd> +<dt><tt class="docutils literal"><span class="pre">--clear-color</span></tt></dt> +<dd>SVG color to use in SVG output for "clear" areas (default: white)</dd> +<dt><tt class="docutils literal"><span class="pre">--dark-color</span></tt></dt> +<dd>SVG color to use in SVG output for "dark" areas (default: black)</dd> +<dt><tt class="docutils literal"><span class="pre">-f,</span> <span class="pre">--flip-gerber-polarity</span></tt></dt> +<dd>Flip polarity of all output gerber primitives for --format gerber.</dd> +<dt><tt class="docutils literal"><span class="pre">-d,</span> <span class="pre">--trace-space</span></tt></dt> +<dd>Minimum feature size of elements in vectorized graphics (trace/space) in mm. Default: 0.1mm.</dd> +<dt><tt class="docutils literal"><span class="pre">--no-header</span></tt></dt> +<dd>Do not export output format header/footer, only export the primitives themselves</dd> +<dt><tt class="docutils literal"><span class="pre">--flatten</span></tt></dt> +<dd>Flatten output so it only consists of non-overlapping white polygons. This perform composition at the vector level. +Potentially slow. This defaults to on when using KiCAD S-Exp export because KiCAD does not know polarity or colors.</dd> +<dt><tt class="docutils literal"><span class="pre">--no-flatten</span></tt></dt> +<dd>Disable automatic flattening for KiCAD S-Exp export</dd> +<dt><tt class="docutils literal"><span class="pre">--dilate</span></tt></dt> +<dd>Dilate output gerber primitives by this amount in mm. Used for masking out other layers.</dd> +<dt><tt class="docutils literal"><span class="pre">-g,</span> <span class="pre">--only-groups</span></tt></dt> +<dd>Comma-separated list of group IDs to export.</dd> +<dt><tt class="docutils literal"><span class="pre">-b,</span> <span class="pre">--vectorizer</span></tt></dt> +<dd>Vectorizer to use for bitmap images. One of poisson-disc (default), hex-grid, square-grid, binary-contours, +dev-null. Have a look at <a class="reference internal" href="#vectorization">the examples below</a>.</dd> +<dt><tt class="docutils literal"><span class="pre">--vectorizer-map</span></tt></dt> +<dd><p class="first">Map from image element id to vectorizer. Overrides --vectorizer. Format: id1=vectorizer,id2=vectorizer,...</p> +<p class="last">You can use this to set a certain vectorizer for specific images, e.g. if you want to use both halftone +vectorization and contour tracing in the same SVG. Note that you can set an <tt class="docutils literal"><image></tt> element's SVG ID from within +Inkscape though the context menu's Object Properties tool.</p> +</dd> +<dt><tt class="docutils literal"><span class="pre">--force-svg</span></tt></dt> +<dd>Force SVG input irrespective of file name</dd> +<dt><tt class="docutils literal"><span class="pre">--force-png</span></tt></dt> +<dd>Force bitmap graphics input irrespective of file name</dd> +<dt><tt class="docutils literal"><span class="pre">-s,</span> <span class="pre">--size</span></tt></dt> +<dd>Bitmap mode only: Physical size of output image in mm. Format: 12.34x56.78</dd> +<dt><tt class="docutils literal"><span class="pre">--sexp-mod-name</span></tt></dt> +<dd>Module name for KiCAD S-Exp output. This is a mandatory argument if using S-Exp output.</dd> +<dt><tt class="docutils literal"><span class="pre">--sexp-layer</span></tt></dt> +<dd>Layer for KiCAD S-Exp output. Defaults to auto-detect layers from SVG layer/top-level group IDs. If given, SVG +groups and layers are completely ignored and everything is simply vectorized into this layer, though you cna still +use <tt class="docutils literal"><span class="pre">-g</span></tt> for group selection.</dd> +<dt><tt class="docutils literal"><span class="pre">-a,</span> <span class="pre">--preserve-aspect-ratio</span></tt></dt> +<dd>Bitmap mode only: Preserve aspect ratio of image. Allowed values are meet, slice. Can also parse full SVG +preserveAspectRatio syntax.</dd> +<dt><tt class="docutils literal"><span class="pre">--no-usvg</span></tt></dt> +<dd>Do not preprocess input using usvg (do not use unless you know <em>exactly</em> what you're doing)</dd> +<dt><tt class="docutils literal"><span class="pre">--usvg-dpi</span></tt></dt> +<dd>Passed through to usvg's --dpi, in case the input file has different ideas of DPI than usvg has.</dd> +<dt><tt class="docutils literal"><span class="pre">--scale</span></tt></dt> +<dd>Scale input svg lengths by this factor (-o gerber only).</dd> +<dt><tt class="docutils literal"><span class="pre">-e,</span> <span class="pre">--exclude-groups</span></tt></dt> +<dd>Comma-separated list of group IDs to exclude from export. Takes precedence over --only-groups.</dd> +</dl> +</div> +</div> +</div> +<div class="section" id="gerbolyze-image-vectorization"> +<span id="vectorization"></span><h2><a class="toc-backref" href="#toc-entry-17">Gerbolyze image vectorization</a></h2> +<p>Gerbolyze has two built-in strategies to translate pixel images into vector images. One is its built-in halftone +processor that tries to approximate grayscale. The other is its built-in binary vectorizer that traces contours in +black-and-white images. Below are examples for the four options.</p> +<p>The vectorizers can be used in isolation through <tt class="docutils literal"><span class="pre">svg-flatten</span></tt> with either an SVG input that contains an image or a +PNG/JPG input.</p> +<p>The vectorizer can be controlled globally using the <tt class="docutils literal"><span class="pre">--vectorizer</span></tt> flag in both <tt class="docutils literal">gerbolyze</tt> and <tt class="docutils literal"><span class="pre">svg-flatten</span></tt>. It +can also be set on a per-image basis in both using <tt class="docutils literal"><span class="pre">--vectorizer-map</span> [image svg <span class="pre">id]=[option][","</span> <span class="pre">...]</span></tt>.</p> +<!-- for f in vec_*.png; convert -background white -gravity center $f -resize 500x500 -extent 500x500 (basename -s .png $f)-square.png; end --> +<!-- for vec in hexgrid square poisson contours; convert vec_"$vec"_whole-square.png vec_"$vec"_detail-square.png -background transparent -splice 25x0+0+0 +append -chop 25x0+0+0 vec_"$vec"_composited.png; end --> +<div class="section" id="vectorizer-poisson-disc-the-default"> +<h3><a class="toc-backref" href="#toc-entry-18"><tt class="docutils literal"><span class="pre">--vectorizer</span> <span class="pre">poisson-disc</span></tt> (the default)</a></h3> +<img alt="pics/vec_poisson_composited.png" src="pics/vec_poisson_composited.png" style="width: 800px;" /> +</div> +<div class="section" id="vectorizer-hex-grid"> +<h3><a class="toc-backref" href="#toc-entry-19"><tt class="docutils literal"><span class="pre">--vectorizer</span> <span class="pre">hex-grid</span></tt></a></h3> +<img alt="pics/vec_hexgrid_composited.png" src="pics/vec_hexgrid_composited.png" style="width: 800px;" /> +</div> +<div class="section" id="vectorizer-square-grid"> +<h3><a class="toc-backref" href="#toc-entry-20"><tt class="docutils literal"><span class="pre">--vectorizer</span> <span class="pre">square-grid</span></tt></a></h3> +<img alt="pics/vec_square_composited.png" src="pics/vec_square_composited.png" style="width: 800px;" /> +</div> +<div class="section" id="vectorizer-binary-contours"> +<h3><a class="toc-backref" href="#toc-entry-21"><tt class="docutils literal"><span class="pre">--vectorizer</span> <span class="pre">binary-contours</span></tt></a></h3> +<img alt="pics/vec_contours_composited.png" src="pics/vec_contours_composited.png" style="width: 800px;" /> +<p>The binary contours vectorizer requires a black-and-white binary input image. As you can see, like every bitmap tracer +it will produce some artifacts. For artistic input this is usually not too bad as long as the input data is +high-resolution. Antialiased edges in the input image are not only OK, they may even help with an accurate +vectorization.</p> +</div> +</div> +<div class="section" id="gimp-halftone-preprocessing-guide"> +<h2><a class="toc-backref" href="#toc-entry-22">GIMP halftone preprocessing guide</a></h2> +<p>Gerbolyze has its own built-in halftone processor, but you can also use the high-quality "newsprint" filter built into +<a class="reference external" href="https://gimp.org/">GIMP</a> instead if you like. This section will guide you through this. The PNG you get out of this can then be fed into +gerbolyze using <tt class="docutils literal"><span class="pre">--vectorizer</span> <span class="pre">binary-contours</span></tt>.</p> +<div class="section" id="import-your-desired-artwork"> +<h3><a class="toc-backref" href="#toc-entry-23">1 Import your desired artwork</a></h3> +<p>Though anime or manga pictures are highly recommended, you can use any image including photographs. Be careful to select +a picture with comparatively low detail that remains recognizable at very low resolution. While working on a screen this +is hard to vizualize, but the grain resulting from the low resolution of a PCB's silkscreen is quite coarse.</p> +<img alt="screenshots/02import02.png" src="screenshots/02import02.png" style="width: 800px;" /> +</div> +<div class="section" id="convert-the-image-to-grayscale"> +<h3><a class="toc-backref" href="#toc-entry-24">2 Convert the image to grayscale</a></h3> +<img alt="screenshots/06grayscale.png" src="screenshots/06grayscale.png" style="width: 800px;" /> +</div> +<div class="section" id="fine-tune-the-image-s-contrast"> +<h3><a class="toc-backref" href="#toc-entry-25">3 Fine-tune the image's contrast</a></h3> +<p>To look well on the PCB, contrast is critical. If your source image is in color, you may have lost some contrast during +grayscale conversion. Now is the time to retouch that using the GIMP's color curve tool.</p> +<p>When using the GIMP's newsprint filter, bright grays close to white and dark grays close to black will cause very small +dots that might be beyond your PCB manufacturer's maximum resolution. To control this case, add small steps at the ends +of the grayscale value curve as shown (exaggerated) in the picture below. These steps saturate very bright grays to +white and very dark grays to black while preserving the values in the middle.</p> +<img alt="screenshots/08curve_cut.png" src="screenshots/08curve_cut.png" style="width: 800px;" /> +</div> +<div class="section" id="retouch-details"> +<h3><a class="toc-backref" href="#toc-entry-26">4 Retouch details</a></h3> +<p>Therer might be small details that don't look right yet, such as the image's background color or small highlights that +merge into the background now. You can manually change the color of any detail now using the GIMP's flood-fill tool.</p> +<p>If you don't want the image's background to show up on the final PCB at all, just make it black.</p> +<p>Particularly on low-resolution source images it may make sense to apply a blur with a radius similar to the following +newsprint filter's cell size (10px) to smooth out the dot pattern generated by the newsprint filter.</p> +<img alt="screenshots/09retouch.png" src="screenshots/09retouch.png" style="width: 800px;" /> +<p>In the following example, I retouched the highlights in the hair of the character in the picture to make them completely +white instead of light-gray, so they still stand out nicely in the finished picture.</p> +<img alt="screenshots/10retouched.png" src="screenshots/10retouched.png" style="width: 800px;" /> +</div> +<div class="section" id="run-the-newsprint-filter"> +<h3><a class="toc-backref" href="#toc-entry-27">5 Run the newsprint filter</a></h3> +<p>Now, run the GIMP's newsprint filter, under filters, distorts, newsprint.</p> +<p>The first important settings is the spot size, which should be larger than your PCB's minimum detail size (about 10px +with <tt class="docutils literal">gerbolyze render</tt> default settings for good-quality silkscreen). In general the cheap and fast standard option of chinese PCB houses will require a larger detail size, but when you order specialty options like large size, 4-layer or non-green color along with a longer turnaround time you'll get much better-quality silk screen.</p> +<p>The second important setting is oversampling, which should be set to four or slightly higher. This improves the result +of the edge reconstruction of <tt class="docutils literal">gerbolyze vectorize</tt>.</p> +<img alt="screenshots/11newsprint.png" src="screenshots/11newsprint.png" style="width: 800px;" /> +<p>The following are examples on the detail resulting from the newsprint filter.</p> +<img alt="screenshots/12newsprint.png" src="screenshots/12newsprint.png" style="width: 800px;" /> +</div> +<div class="section" id="export-the-image-for-use-with-gerbolyze-vectorize"> +<h3><a class="toc-backref" href="#toc-entry-28">6 Export the image for use with <tt class="docutils literal">gerbolyze vectorize</tt></a></h3> +<p>Simply export the image as a PNG file. Below are some pictures of the output <tt class="docutils literal">gerbolyze vectorize</tt> produced for this +example.</p> +<img alt="screenshots/14result_cut.png" src="screenshots/14result_cut.png" style="width: 800px;" /> +<img alt="screenshots/15result_cut.png" src="screenshots/15result_cut.png" style="width: 800px;" /> +</div> +</div> +<div class="section" id="manufacturing-considerations"> +<h2><a class="toc-backref" href="#toc-entry-29">Manufacturing Considerations</a></h2> +<p>The main consideration when designing artwork for PCB processes is the processes' trace/space design rule. The two +things you can do here is one, to be creative with graphical parts of the design and avoid extremely narrow lines, +wedges or other thin features that will not come out well. Number two is to keep detail in raster images several times +larger than the manufacturing processes native capability. For example, to target a trace/space design rule of 100 µm, +the smallest detail in embedded raster graphics should not be much below 1mm.</p> +<p>Gerbolyze's halftone vectorizers have built-in support for trace/space design rules. While they can still produce small +artifacts that violate these rules, their output should be close enough to satifsy board houses and close enough for the +result to look good. The way gerbolyze does this is to clip the halftone cell's values to zero whenevery they get too +small, and to forcefully split or merge two neighboring cells when they get too close. While this process introduces +slight steps at the top and bottom of grayscale response, for most inputs these are not noticeable.</p> +<p>On the other hand, for SVG vector elements as well as for traced raster images, Gerbolyze cannot help with these design +rules. There is no heuristic that would allow Gerbolyze to non-destructively "fix" a design here, so all that's on the +roadmap here is to eventually include a gerber-level design rule checker.</p> +<p>As far as board houses go, I have made good experiences with the popular Chinese board houses. In my experience, JLC +will just produce whatever you send them with little fucks being given about design rule adherence or validity of the +input gerbers. This is great if you just want artistic circuit boards without much of a hassle, and you don't care if +they come out exactly as you imagined. The worst I've had happen was when an older version of gerbolyze generated +polygons with holes assuming standard fill-rule processing. The in the board house's online gerber viewer things looked +fine, and neither did they complain during file review. However, the resulting boards looked completely wrong because +all the dark halftones were missing.</p> +<p>PCBWay on the other hand has a much more rigurous file review process. They <em>will</em> complain when you throw +illegal garbage gerbers at them, and they will helpfully guide you through your design rule violations. In this way you +get much more of a professional service from them and for designs that have to be functional their higher level of +scrutiny definitely is a good thing. For the design you saw in the first picture in this article, I ended up begging +them to just plot my files if it doesn't physically break their machines and to their credit, while they seemed unhappy +about it they did it and the result looks absolutely stunning.</p> +<p>PCBWay is a bit more expensive on their lowest-end offering than JLC, but I found that for anything else (large boards, +multi-layer, gold plating etc.) their prices match. PCBWay offers a much broader range of manufacturing options such as +flexible circuit boards, multi-layer boards, thick or thin substrates and high-temperature substrates.</p> +<p>When in doubt about how your design is going to come out on the board, do not hesitate to contact your board house. Most +of the end customer-facing online PCB services have a number of different factories that do a number of different +fabrication processes for them depending on order parameters. Places like PCBWay have exceptional quality control and +good customer service, but that is mostly focused on the technical aspects of the PCB. If you rely on visual aspects +like silkscreen uniformity or solder mask color that is a strong no concern to everyone else in the electronics +industry, you may find significant variations between manufacturers or even between orders with the same manufacturer +and you may encounter challenges communicating your requirements.</p> +</div> +<div class="section" id="limitations"> +<h2><a class="toc-backref" href="#toc-entry-30">Limitations</a></h2> +<div class="section" id="svg-raster-features"> +<h3><a class="toc-backref" href="#toc-entry-31">SVG raster features</a></h3> +<p>Currently, SVG masks and filters are not supported. Though SVG is marketed as a "vector graphics format", these two +features are really raster primitives that all SVG viewers perform at the pixel level after rasterization. Since +supporting these would likely not end up looking like what you want, it is not a planned feature. If you need masks or +filters, simply export the relevant parts of the SVG as a PNG then include that in your template.</p> +</div> +<div class="section" id="gerber-pass-through"> +<h3><a class="toc-backref" href="#toc-entry-32">Gerber pass-through</a></h3> +<p>Since gerbolyze has to composite your input gerbers with its own output, it has to fully parse and re-serialize them. +gerbolyze <a class="reference external" href="https://gitlab.com/gerbolyze/gerbonara">gerbonara</a> for all its gerber parsing needs. Thus, gerbonara will interpret your gerbers and output will be in +gerbonara's gerber "dialect". If you find a corner case where this does not work and the output looks wrong, please file +a bug report with an example file on the <a class="reference external" href="https://gitlab.com/gerbolyze/gerbonara">gerbonara</a> bug tracker. <em>Always</em> check the output files for errors before +submitting them to production.</p> +<p>Gerbolyze is provided without any warranty, but still please open an issue or <a class="reference external" href="mailto:gerbolyze@jaseg.de">send me an email</a> if you find any errors or inconsistencies.</p> +</div> +<div class="section" id="trace-space-design-rule-adherence"> +<h3><a class="toc-backref" href="#toc-entry-33">Trace/Space design rule adherence</a></h3> +<p>While the grayscale halftone vectorizers do a reasonable job adhering to a given trace/space design rule, they can still +produce small parts of output that violate it. For the contour vectorizer as well as for all SVG primitives, you are +responsible for adhering to design rules yourself as there is no algorithm that gerboyze could use to "fix" its input.</p> +<p>A design rule checker is planned as a future addition to gerbolyze, but is not yet part of it. If in doubt, talk to your +fab and consider doing a test run of your design before ordering assembled boards ;)</p> +</div> +</div> +<div class="section" id="gallery"> +<h2><a class="toc-backref" href="#toc-entry-34">Gallery</a></h2> +<img alt="pics/sample3.jpg" src="pics/sample3.jpg" style="width: 400px;" /> +<p>For a demonstration of <tt class="docutils literal">gerbolyze convert</tt>, check out the <a class="reference external" href="https://dyna.kokoroyukuma.de/protos/">Gerbolyze Protoboard Index</a>, where you can download gerber +files for over 7.000 SMD and THT protoboard layouts.</p> +</div> +<div class="section" id="licensing"> +<h2><a class="toc-backref" href="#toc-entry-35">Licensing</a></h2> +<p>This tool is licensed under the rather radical AGPLv3 license. Briefly, this means that you have to provide users of a +webapp using this tool in the backend with this tool's source.</p> +<p>I get that some people have issues with the AGPL. In case this license prevents you from using this software, please +send me <a class="reference external" href="mailto:agpl.sucks@jaseg.de">an email</a> and I can grant you an exception. I want this software to be useful to as +many people as possible and I wouldn't want the license to be a hurdle to anyone. OTOH I see a danger of some cheap +board house just integrating a fork into their webpage without providing their changes back upstream, and I want to +avoid that so the default license is still AGPL.</p> +</div> +</div> + </main><footer> + Copyright © 2023 Jan Sebastian Götte + / <a href="/about/">About</a> + / <a href="/imprint/">Imprint</a> +</footer> +<script> + if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser + contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to + prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition. + In support of an open web, this website does not function with this DRM. Please install a browser such + as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports + ad blockers.`; + </script> + </body> +</html> diff --git a/content/projects/gerbolyze/pics/ex-flattening.png b/projects/gerbolyze/pics/ex-flattening.png Binary files differindex e702733..e702733 100644 --- a/content/projects/gerbolyze/pics/ex-flattening.png +++ b/projects/gerbolyze/pics/ex-flattening.png diff --git a/content/projects/gerbolyze/pics/ex-intersections.png b/projects/gerbolyze/pics/ex-intersections.png Binary files differindex 05ccc59..05ccc59 100644 --- a/content/projects/gerbolyze/pics/ex-intersections.png +++ b/projects/gerbolyze/pics/ex-intersections.png diff --git a/content/projects/gerbolyze/pics/ex-strokes.png b/projects/gerbolyze/pics/ex-strokes.png Binary files differindex 5ef3c49..5ef3c49 100644 --- a/content/projects/gerbolyze/pics/ex-strokes.png +++ b/projects/gerbolyze/pics/ex-strokes.png diff --git a/content/projects/gerbolyze/pics/ex-svg-joins.png b/projects/gerbolyze/pics/ex-svg-joins.png Binary files differindex 534e4c2..534e4c2 100644 --- a/content/projects/gerbolyze/pics/ex-svg-joins.png +++ b/projects/gerbolyze/pics/ex-svg-joins.png diff --git a/content/projects/gerbolyze/pics/ex-svg-strokes.png b/projects/gerbolyze/pics/ex-svg-strokes.png Binary files differindex bec71c0..bec71c0 100644 --- a/content/projects/gerbolyze/pics/ex-svg-strokes.png +++ b/projects/gerbolyze/pics/ex-svg-strokes.png diff --git a/content/projects/gerbolyze/pics/ex-svg-winding.png b/projects/gerbolyze/pics/ex-svg-winding.png Binary files differindex 3f85f6f..3f85f6f 100644 --- a/content/projects/gerbolyze/pics/ex-svg-winding.png +++ b/projects/gerbolyze/pics/ex-svg-winding.png diff --git a/content/projects/gerbolyze/pics/fr4_comparison2.jpg b/projects/gerbolyze/pics/fr4_comparison2.jpg Binary files differindex fa7c92e..fa7c92e 100644 --- a/content/projects/gerbolyze/pics/fr4_comparison2.jpg +++ b/projects/gerbolyze/pics/fr4_comparison2.jpg diff --git a/content/projects/gerbolyze/pics/pcbway_sample_01_small.jpg b/projects/gerbolyze/pics/pcbway_sample_01_small.jpg Binary files differindex f8d6397..f8d6397 100644 --- a/content/projects/gerbolyze/pics/pcbway_sample_01_small.jpg +++ b/projects/gerbolyze/pics/pcbway_sample_01_small.jpg diff --git a/content/projects/gerbolyze/pics/pcbway_sample_02_small.jpg b/projects/gerbolyze/pics/pcbway_sample_02_small.jpg Binary files differindex ba8e984..ba8e984 100644 --- a/content/projects/gerbolyze/pics/pcbway_sample_02_small.jpg +++ b/projects/gerbolyze/pics/pcbway_sample_02_small.jpg diff --git a/content/projects/gerbolyze/pics/pcbway_sample_03_small.jpg b/projects/gerbolyze/pics/pcbway_sample_03_small.jpg Binary files differindex 1336739..1336739 100644 --- a/content/projects/gerbolyze/pics/pcbway_sample_03_small.jpg +++ b/projects/gerbolyze/pics/pcbway_sample_03_small.jpg diff --git a/content/projects/gerbolyze/pics/process-overview.png b/projects/gerbolyze/pics/process-overview.png Binary files differindex 01d3811..01d3811 100644 --- a/content/projects/gerbolyze/pics/process-overview.png +++ b/projects/gerbolyze/pics/process-overview.png diff --git a/content/projects/gerbolyze/pics/process-overview.svg b/projects/gerbolyze/pics/process-overview.svg index b21aa70..b21aa70 100644 --- a/content/projects/gerbolyze/pics/process-overview.svg +++ b/projects/gerbolyze/pics/process-overview.svg diff --git a/content/projects/gerbolyze/pics/sample1.jpg b/projects/gerbolyze/pics/sample1.jpg Binary files differindex 948da6f..948da6f 100644 --- a/content/projects/gerbolyze/pics/sample1.jpg +++ b/projects/gerbolyze/pics/sample1.jpg diff --git a/content/projects/gerbolyze/pics/sample2.jpg b/projects/gerbolyze/pics/sample2.jpg Binary files differindex ef47bd4..ef47bd4 100644 --- a/content/projects/gerbolyze/pics/sample2.jpg +++ b/projects/gerbolyze/pics/sample2.jpg diff --git a/content/projects/gerbolyze/pics/sample3.jpg b/projects/gerbolyze/pics/sample3.jpg Binary files differindex 780c080..780c080 100644 --- a/content/projects/gerbolyze/pics/sample3.jpg +++ b/projects/gerbolyze/pics/sample3.jpg diff --git a/content/projects/gerbolyze/pics/subtract_example.png b/projects/gerbolyze/pics/subtract_example.png Binary files differindex f8e138a..f8e138a 100644 --- a/content/projects/gerbolyze/pics/subtract_example.png +++ b/projects/gerbolyze/pics/subtract_example.png diff --git a/content/projects/gerbolyze/pics/test_svg_readme.svg b/projects/gerbolyze/pics/test_svg_readme.svg index 1a0178e..1a0178e 100644 --- a/content/projects/gerbolyze/pics/test_svg_readme.svg +++ b/projects/gerbolyze/pics/test_svg_readme.svg diff --git a/content/projects/gerbolyze/pics/test_svg_readme_composited.png b/projects/gerbolyze/pics/test_svg_readme_composited.png Binary files differindex f686e19..f686e19 100644 --- a/content/projects/gerbolyze/pics/test_svg_readme_composited.png +++ b/projects/gerbolyze/pics/test_svg_readme_composited.png diff --git a/content/projects/gerbolyze/pics/vec_contours_composited.png b/projects/gerbolyze/pics/vec_contours_composited.png Binary files differindex aa826f8..aa826f8 100644 --- a/content/projects/gerbolyze/pics/vec_contours_composited.png +++ b/projects/gerbolyze/pics/vec_contours_composited.png diff --git a/content/projects/gerbolyze/pics/vec_hexgrid_composited.png b/projects/gerbolyze/pics/vec_hexgrid_composited.png Binary files differindex 9578260..9578260 100644 --- a/content/projects/gerbolyze/pics/vec_hexgrid_composited.png +++ b/projects/gerbolyze/pics/vec_hexgrid_composited.png diff --git a/content/projects/gerbolyze/pics/vec_poisson_composited.png b/projects/gerbolyze/pics/vec_poisson_composited.png Binary files differindex e3ac758..e3ac758 100644 --- a/content/projects/gerbolyze/pics/vec_poisson_composited.png +++ b/projects/gerbolyze/pics/vec_poisson_composited.png diff --git a/content/projects/gerbolyze/pics/vec_square_composited.png b/projects/gerbolyze/pics/vec_square_composited.png Binary files differindex 02a89ea..02a89ea 100644 --- a/content/projects/gerbolyze/pics/vec_square_composited.png +++ b/projects/gerbolyze/pics/vec_square_composited.png diff --git a/content/projects/gerbolyze/screenshots/01import01.png b/projects/gerbolyze/screenshots/01import01.png Binary files differindex ca07db3..ca07db3 100644 --- a/content/projects/gerbolyze/screenshots/01import01.png +++ b/projects/gerbolyze/screenshots/01import01.png diff --git a/content/projects/gerbolyze/screenshots/02import02.png b/projects/gerbolyze/screenshots/02import02.png Binary files differindex 0b18110..0b18110 100644 --- a/content/projects/gerbolyze/screenshots/02import02.png +++ b/projects/gerbolyze/screenshots/02import02.png diff --git a/content/projects/gerbolyze/screenshots/03paste.png b/projects/gerbolyze/screenshots/03paste.png Binary files differindex 76e4e59..76e4e59 100644 --- a/content/projects/gerbolyze/screenshots/03paste.png +++ b/projects/gerbolyze/screenshots/03paste.png diff --git a/content/projects/gerbolyze/screenshots/04scale_cut.png b/projects/gerbolyze/screenshots/04scale_cut.png Binary files differindex 3011a8f..3011a8f 100644 --- a/content/projects/gerbolyze/screenshots/04scale_cut.png +++ b/projects/gerbolyze/screenshots/04scale_cut.png diff --git a/content/projects/gerbolyze/screenshots/05position.png b/projects/gerbolyze/screenshots/05position.png Binary files differindex 4044648..4044648 100644 --- a/content/projects/gerbolyze/screenshots/05position.png +++ b/projects/gerbolyze/screenshots/05position.png diff --git a/content/projects/gerbolyze/screenshots/06grayscale.png b/projects/gerbolyze/screenshots/06grayscale.png Binary files differindex cea49a8..cea49a8 100644 --- a/content/projects/gerbolyze/screenshots/06grayscale.png +++ b/projects/gerbolyze/screenshots/06grayscale.png diff --git a/content/projects/gerbolyze/screenshots/07curve_settings.png b/projects/gerbolyze/screenshots/07curve_settings.png Binary files differindex 027362b..027362b 100644 --- a/content/projects/gerbolyze/screenshots/07curve_settings.png +++ b/projects/gerbolyze/screenshots/07curve_settings.png diff --git a/content/projects/gerbolyze/screenshots/08curve_cut.png b/projects/gerbolyze/screenshots/08curve_cut.png Binary files differindex b57c7f5..b57c7f5 100644 --- a/content/projects/gerbolyze/screenshots/08curve_cut.png +++ b/projects/gerbolyze/screenshots/08curve_cut.png diff --git a/content/projects/gerbolyze/screenshots/09retouch.png b/projects/gerbolyze/screenshots/09retouch.png Binary files differindex cdef9bd..cdef9bd 100644 --- a/content/projects/gerbolyze/screenshots/09retouch.png +++ b/projects/gerbolyze/screenshots/09retouch.png diff --git a/content/projects/gerbolyze/screenshots/10retouched.png b/projects/gerbolyze/screenshots/10retouched.png Binary files differindex 6181730..6181730 100644 --- a/content/projects/gerbolyze/screenshots/10retouched.png +++ b/projects/gerbolyze/screenshots/10retouched.png diff --git a/content/projects/gerbolyze/screenshots/11newsprint.png b/projects/gerbolyze/screenshots/11newsprint.png Binary files differindex 6a371d5..6a371d5 100644 --- a/content/projects/gerbolyze/screenshots/11newsprint.png +++ b/projects/gerbolyze/screenshots/11newsprint.png diff --git a/content/projects/gerbolyze/screenshots/11newsprint_settings.png b/projects/gerbolyze/screenshots/11newsprint_settings.png Binary files differindex 55ae6cf..55ae6cf 100644 --- a/content/projects/gerbolyze/screenshots/11newsprint_settings.png +++ b/projects/gerbolyze/screenshots/11newsprint_settings.png diff --git a/content/projects/gerbolyze/screenshots/12newsprint.png b/projects/gerbolyze/screenshots/12newsprint.png Binary files differindex 9a33162..9a33162 100644 --- a/content/projects/gerbolyze/screenshots/12newsprint.png +++ b/projects/gerbolyze/screenshots/12newsprint.png diff --git a/content/projects/gerbolyze/screenshots/13newsprint.png b/projects/gerbolyze/screenshots/13newsprint.png Binary files differindex 03d58b7..03d58b7 100644 --- a/content/projects/gerbolyze/screenshots/13newsprint.png +++ b/projects/gerbolyze/screenshots/13newsprint.png diff --git a/content/projects/gerbolyze/screenshots/14newsprint.png b/projects/gerbolyze/screenshots/14newsprint.png Binary files differindex 4a5e517..4a5e517 100644 --- a/content/projects/gerbolyze/screenshots/14newsprint.png +++ b/projects/gerbolyze/screenshots/14newsprint.png diff --git a/content/projects/gerbolyze/screenshots/14result_cut.png b/projects/gerbolyze/screenshots/14result_cut.png Binary files differindex addb140..addb140 100644 --- a/content/projects/gerbolyze/screenshots/14result_cut.png +++ b/projects/gerbolyze/screenshots/14result_cut.png diff --git a/content/projects/gerbolyze/screenshots/15result_cut.png b/projects/gerbolyze/screenshots/15result_cut.png Binary files differindex 92e3107..92e3107 100644 --- a/content/projects/gerbolyze/screenshots/15result_cut.png +++ b/projects/gerbolyze/screenshots/15result_cut.png diff --git a/content/projects/gerbolyze/screenshots/16result_cut.png b/projects/gerbolyze/screenshots/16result_cut.png Binary files differindex 52cf806..52cf806 100644 --- a/content/projects/gerbolyze/screenshots/16result_cut.png +++ b/projects/gerbolyze/screenshots/16result_cut.png diff --git a/content/projects/gerbolyze/screenshots/17caveat_cut.png b/projects/gerbolyze/screenshots/17caveat_cut.png Binary files differindex e0f2aa9..e0f2aa9 100644 --- a/content/projects/gerbolyze/screenshots/17caveat_cut.png +++ b/projects/gerbolyze/screenshots/17caveat_cut.png diff --git a/content/projects/gerbolyze/screenshots/18caveat_cut.png b/projects/gerbolyze/screenshots/18caveat_cut.png Binary files differindex b3d7802..b3d7802 100644 --- a/content/projects/gerbolyze/screenshots/18caveat_cut.png +++ b/projects/gerbolyze/screenshots/18caveat_cut.png diff --git a/projects/gerbonara/index.html b/projects/gerbonara/index.html new file mode 100644 index 0000000..266a1d2 --- /dev/null +++ b/projects/gerbonara/index.html @@ -0,0 +1,165 @@ +<!DOCTYPE html> +<html><head> + <meta charset="utf-8"> + <title>Gerbonara | Home</title> + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="mobile-web-app-capable" content="yes"> + <meta name="color-scheme" content="dark light"> + <link rel="stylesheet" href="/style.css"> +</head> +<body><nav> + <div class="internal"> + + <a href="/" title="Home">Home</a> + <a href="/blog/" title="Blog">Blog</a> + <a href="/projects/" title="Projects">Projects</a> + <a href="/about/" title="About">About</a> + </div> + <div class="external"> + <a href="https://git.jaseg.de/" title="cgit">cgit</a> + <a href="https://github.com/jaseg" title="Github">Github</a> + <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a> + <a href="https://chaos.social/jaseg" title="Mastodon">Mastodon</a> + </span> +</nav> + + <header> + <h1>Gerbonara</h1> +<ul class="breadcrumbs"> + <li><a href="/">jaseg.de</a></li> + <li><a href="/projects/">Projects</a></li><li><a href="/projects/gerbonara/">Gerbonara</a></li> +</ul> + + </header> + <main> + <div class="links"> + <a href="https://git.jaseg.de/gerbonara.git">Sources</a> + <a href="https://gitlab.com/gerbolyze/gerbonara/issues">Issues</a> + <a href="https://gerbolyze.gitlab.io/gerbonara">Docs</a> + <a href="https://pypi.org/project/gerbonara">PyPI</a> + </div> + <div class="document"> + + +<p>Gerbonara is a library to read, modify and write PCB manufacturing files such as Gerber, Excellon and IPC-356 through a +pythonic API. Gerbonara can open a folder of manufacturing files, and parse file names and metadata to figure out which +file contains what. Gerbonara is tested using an extensive library of real-world example files from CAD tools including +KiCAD, Altium, Eagle, Allegro, gEDA, Fritzing, Siemens/Mentor Graphics PADS, and Target3001!.</p> +<p>Gerbonara's API is built on two principles:</p> +<dl class="docutils"> +<dt><strong>Meaningful, object-oriented API</strong></dt> +<dd>Gerbonara abstracts away the details of the underlying file format such as tool indices, coordinate notation and +graphical state, and presents meaningful "graphical objects" such as a <cite>primitives.Line</cite>, +<cite>primitives.Arc</cite>, or <cite>Region</cite> through its API. These objects can be easily created, +manipulated or deleted from code without breaking anything else. You can even copy graphical objects between files, +and Gerbonara will automatically convert coordinate format, units etc. for you. <cite>GerberFile</cite> and +<cite>ExcellonFile</cite> use the same types of <cite>graphic objects <object-api></cite>, so objects can be directly +copied between file types without conversion.</dd> +<dt><strong>Unit-safety</strong></dt> +<dd>Gerbonara embeds physical <cite>LengthUnit</cite> information in all objects. The high-level API such as +<cite>LayerStack.merge</cite> or <cite>GerberFile.offset</cite> accepts arguments with an explicitly given unit and +automatically converts them as needed. Objects can be copied between <cite>GerberFile</cite> instances and unit +conversion will be handled transparently in the background.</dd> +</dl> +<p>Gerbonara was started as an extensive refactoring of the <a class="reference external" href="https://github.com/opiopan/pcb-tools-extension">pcb-tools</a> and <a class="reference external" href="https://github.com/curtacircuitos/pcb-tools/issues">pcb-tools-extension</a> packages. Both of these +have statement-based APIs, that is, they parse input files into one python object for every line in the file. This means +that when saving files they can recreate the input file almost byte by byte, but manipulating a file by changing +statements without breaking things is <em>hard</em>.</p> +<p>Gerbonara powers <a class="reference external" href="https://github.com/jaseg/gerbolyze">gerbolyze</a>, a tool for converting <a class="reference external" href="https://en.wikipedia.org/wiki/Scalable_Vector_Graphics">SVG</a> vector graphics files into Gerber, and embedding <a class="reference external" href="https://en.wikipedia.org/wiki/Scalable_Vector_Graphics">SVG</a> into +existing Gerber files exported from a normal PCB tool for artistic purposes.</p> +<div class="section" id="features"> +<h2>Features</h2> +<blockquote> +<ul class="simple"> +<li>File I/O +* Gerber, Excellon (drill file), IPC-356 (netlist) read and write +* supports file-level operations: offset, rotate, merge for all file types</li> +<li>Modification API (<cite>GraphicObject</cite>)</li> +<li>Rendering API (<cite>GraphicPrimitive</cite>)</li> +<li>SVG export</li> +<li>Full aperture macro support, including transformations (offset, rotation)</li> +</ul> +</blockquote> +</div> +<div class="section" id="quick-start"> +<h2>Quick Start</h2> +<p>First, install gerbonara from PyPI using pip:</p> +<pre class="code shell literal-block"> +<span class="lineno"></span><span class="line">pip<span class="w"> </span>install<span class="w"> </span>--user<span class="w"> </span>gerbonara +</span></pre> +<p>Then, you are ready to read and write gerber files:</p> +<pre class="code python literal-block"> +<span class="lineno"></span><span class="line"><span class="kn">from</span> <span class="nn">gerbonara</span> <span class="kn">import</span> <span class="n">LayerStack</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">stack</span> <span class="o">=</span> <span class="n">LayerStack</span><span class="o">.</span><span class="n">from_directory</span><span class="p">(</span><span class="s1">'output/gerber'</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">w</span><span class="p">,</span> <span class="n">h</span> <span class="o">=</span> <span class="n">stack</span><span class="o">.</span><span class="n">outline</span><span class="o">.</span><span class="n">size</span><span class="p">(</span><span class="s1">'mm'</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">'Board size is </span><span class="si">{</span><span class="n">w</span><span class="si">:</span><span class="s1">.1f</span><span class="si">}</span><span class="s1"> mm x </span><span class="si">{</span><span class="n">h</span><span class="si">:</span><span class="s1">.1f</span><span class="si">}</span><span class="s1"> mm'</span><span class="p">)</span> +</span></pre> +</div> +<div class="section" id="command-line-interface"> +<h2>Command-Line Interface</h2> +<p>Gerbonara comes with a <cite>built-in command-line interface<cli-doc></cite> that has functions for analyzing, rendering, +modifying, and merging Gerber files. To access it, use either the <tt class="docutils literal">gerbonara</tt> command that is part of the python +package, or run <tt class="docutils literal">python <span class="pre">-m</span> gerbonara</tt> For a list of functions or help on their usage, you can use:</p> +<pre class="code console literal-block"> +<span class="lineno"></span><span class="line"><span class="gp">$ </span>python<span class="w"> </span>-m<span class="w"> </span>gerbonara<span class="w"> </span>--help<span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="go">[...]</span></span> +<span class="lineno"></span><span class="line"><span class="go"></span><span class="gp">$ </span>python<span class="w"> </span>-m<span class="w"> </span>gerbonara<span class="w"> </span>render<span class="w"> </span>--help +</span></pre> +</div> +<div class="section" id="development"> +<h2>Development</h2> +<p>Gerbonara is developed on Gitlab under the gerbolyze org:</p> +<p><a class="reference external" href="https://gitlab.com/gerbolyze/gerbonara/">https://gitlab.com/gerbolyze/gerbonara/</a></p> +<p>A mirror of the repository can be found at:</p> +<p><a class="reference external" href="https://git.jaseg.de/gerbonara">https://git.jaseg.de/gerbonara</a></p> +<p>Our issue tracker is also on Gitlab:</p> +<p><a class="reference external" href="https://gitlab.com/gerbolyze/gerbonara/-/issues">https://gitlab.com/gerbolyze/gerbonara/-/issues</a></p> +<p>The documentation can be found at gitlab:</p> +<p><a class="reference external" href="https://gerbolyze.gitlab.io/gerbonara/">https://gerbolyze.gitlab.io/gerbonara/</a></p> +<p>With Gerbonara, we aim to support as many different format variants as possible. If you have a file that Gerbonara can't +open, please file an issue on our issue tracker. Even if Gerbonara can open all your files, for regression testing we +are very interested in example files generated by any CAD or CAM tool that is not already on the list of supported +tools.</p> +</div> +<div class="section" id="supported-cad-tools"> +<h2>Supported CAD Tools</h2> +<p>Compatibility with the output of these CAD tools is tested as part of our test suite using example files generated by +these tools. Note that not all of these tools come with default Gerber file naming rules, so YMMV if your Gerbers use +some non-standard naming convention.</p> +<blockquote> +<ul class="simple"> +<li>Allegro</li> +<li>Altium</li> +<li>Diptrace</li> +<li>Eagle</li> +<li>EasyEDA</li> +<li>Fritzing</li> +<li>gEDA</li> +<li>KiCAD</li> +<li>pcb-rnd</li> +<li>Siemens / Mentor Graphics Xpedition</li> +<li>Siemens PADS</li> +<li>Target 3001!</li> +<li>Upverter</li> +<li>Zuken CR-8000</li> +</ul> +</blockquote> +</div> +</div> + </main><footer> + Copyright © 2023 Jan Sebastian Götte + / <a href="/about/">About</a> + / <a href="/imprint/">Imprint</a> +</footer> +<script> + if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser + contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to + prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition. + In support of an open web, this website does not function with this DRM. Please install a browser such + as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports + ad blockers.`; + </script> + </body> +</html> diff --git a/projects/index.html b/projects/index.html new file mode 100644 index 0000000..5376a7a --- /dev/null +++ b/projects/index.html @@ -0,0 +1,180 @@ +<!DOCTYPE html> +<html><head> + <meta charset="utf-8"> + <title>Projects | Home</title> + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="mobile-web-app-capable" content="yes"> + <meta name="color-scheme" content="dark light"> + <link rel="stylesheet" href="/style.css"> +</head> +<body><nav> + <div class="internal"> + + <a href="/" title="Home">Home</a> + <a href="/blog/" title="Blog">Blog</a> + <a href="/projects/" title="Projects" class="active">Projects</a> + <a href="/about/" title="About">About</a> + </div> + <div class="external"> + <a href="https://git.jaseg.de/" title="cgit">cgit</a> + <a href="https://github.com/jaseg" title="Github">Github</a> + <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a> + <a href="https://chaos.social/jaseg" title="Mastodon">Mastodon</a> + </span> +</nav> + + <header> + <h1>Projects</h1> +<ul class="breadcrumbs"> + <li><a href="/">jaseg.de</a></li><li><a href="/projects/">Projects</a></li> +</ul> + + </header> + <main class="cards"> + <div class="intro"> + <div class="document"> + + +<p>I maintain a number of open-source projects. Most of these I started out of some personal need or interest. +I strive to keep all of them up to date and maintained, so if you notice an issue with one of them, please +open an issue on the project's issue tracker.</p> +</div> + </div> + <div class="card"><h3><a href="/projects/kimesh/">KiMesh</a></h3> + + <div class="summary"> + <div class="document"> + + +<p>KiMesh is a KiCad plugin that automatically creates security meshes with two or traces covering an arbitrarily-shaped outline on the board.</p> +</div> + <a href="http://jaseg.de/projects/kimesh/">Read more</a> + </div> + <div class="links"> + <a href="https://git.jaseg.de/kimesh.git">Sources</a> + <a href="https://github.com/jaseg/kimesh/issues">Issues</a> + <a href="https://jaseg.de/projects/kimesh">Docs</a> + </div> +</div> + + <div class="card"><h3><a href="/projects/gerbolyze/">Gerbolyze</a></h3> + + <div class="summary"> + <div class="document"> + + +<p>Gerbolyze is a tool that allows the modification of Gerber PCB artwork with a vector graphics editor like Inkscape. Gerbolyze directly converts between SVG and Gerber, and accurately reproduces details that other tools can not.</p> +</div> + <a href="http://jaseg.de/projects/gerbolyze/">Read more</a> + </div> + <div class="links"> + <a href="https://git.jaseg.de/gerbolyze.git">Sources</a> + <a href="https://github.com/jaseg/gerbolyze/issues">Issues</a> + <a href="https://gerbolyze.gitlab.io/gerbolyze">Docs</a> + <a href="https://pypi.org/project/gerbolyze">PyPI</a> + </div> +</div> + + <div class="card"><h3><a href="/projects/gerbonara/">Gerbonara</a></h3> + + <div class="summary"> + <div class="document"> + + +<p>Gerbonara is a user-friendly, powerful tool for reading, writing, modification and rendering of Gerber PCB artwork from the command line or from Python code. Gerbonara supports the Gerber dialects of all industry-standard EDA tools.</p> +</div> + <a href="http://jaseg.de/projects/gerbonara/">Read more</a> + </div> + <div class="links"> + <a href="https://git.jaseg.de/gerbonara.git">Sources</a> + <a href="https://gitlab.com/gerbolyze/gerbonara/issues">Issues</a> + <a href="https://gerbolyze.gitlab.io/gerbonara">Docs</a> + <a href="https://pypi.org/project/gerbonara">PyPI</a> + </div> +</div> + + <div class="card"><h3><a href="/projects/lolcat-c/">lolcat-c</a></h3> + + <div class="summary"> + <div class="document"> + + +<p>lolcat-c is a small, high-performance re-implementation of the <a class="reference external" href="https://github.com/busyloop/lolcat">lolcat</a> rainbow cat utility. lolcat-c is meant as a lolcat that you can actually use in production. It is fast, not slowing down whatever you pipe through it, and it robustly handles real-world terminal output including escape sequences.</p> +</div> + <a href="http://jaseg.de/projects/lolcat-c/">Read more</a> + </div> + <div class="links"> + <a href="https://git.jaseg.de/lolcat.git">Sources</a> + <a href="https://github.com/jaseg/lolcat">Github</a> + <a href="https://github.com/jaseg/lolcat/issues">Issues</a> + </div> +</div> + + <div class="card"><h3><a href="/projects/python-mpv/">python-mpv</a></h3> + + <div class="summary"> + <div class="document"> + + +<p>python-mpv is a small, ctypes-based Python library wrapping the libmpv media player library. Despite its small size and simple API, python-mpv allows advanced control over libmpv and beyond simple remote control of mpv can be used to embed mpv in OpenGL, Qt, and GTK-based Python applications.</p> +</div> + <a href="http://jaseg.de/projects/python-mpv/">Read more</a> + </div> + <div class="links"> + <a href="https://git.jaseg.de/python-mpv.git">Sources</a> + <a href="https://github.com/jaseg/python-mpv/issues">Issues</a> + <a href="https://neinseg.gitlab.io/python-mpv">Docs</a> + <a href="https://pypi.org/project/mpv">PyPI</a> + </div> +</div> + + <div class="card"><h3><a href="/projects/svg-flatten/">svg-flatten</a></h3> + + <div class="summary"> + <div class="document"> + + +<p>svg-flatten is a command-line utility that performs vector occlusion and clipping on SVG files, producing a flattened SVG file without overlapping elements, without changing what the file looks like. svg-flatten is used as a part of gerbolyze.</p> +</div> + <a href="http://jaseg.de/projects/svg-flatten/">Read more</a> + </div> + <div class="links"> + <a href="https://git.jaseg.de/gerbolyze.git/tree/svg-flatten?h=main">Sources</a> + <a href="https://github.com/jaseg/gerbolyze/issues">Issues</a> + <a href="https://gerbolyze.gitlab.io/svg-flatten">Docs</a> + </div> +</div> + + <div class="card"><h3><a href="/projects/wsdiff/">wsdiff</a></h3> + + <div class="summary"> + <div class="document"> + + +<p>wsdiff is a command-line utility that produces self-contained, syntax-highlighted, HTML-formatted diffs that support both unified and side-by-side diffs from a single source file using nothing but CSS magic.</p> +</div> + <a href="http://jaseg.de/projects/wsdiff/">Read more</a> + </div> + <div class="links"> + <a href="https://git.jaseg.de/wsdiff.git">Sources</a> + <a href="https://github.com/jaseg/wsdiff/issues">Issues</a> + <a href="https://pypi.org/project/wsdiff">PyPI</a> + </div> +</div> + + </main><footer> + Copyright © 2023 Jan Sebastian Götte + / <a href="/about/">About</a> + / <a href="/imprint/">Imprint</a> +</footer> +<script> + if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser + contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to + prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition. + In support of an open web, this website does not function with this DRM. Please install a browser such + as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports + ad blockers.`; + </script> + </body> +</html> diff --git a/projects/index.xml b/projects/index.xml new file mode 100644 index 0000000..ee4fa9d --- /dev/null +++ b/projects/index.xml @@ -0,0 +1,103 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> + <channel> + <title>Projects on Home</title> + <link>http://jaseg.de/projects/</link> + <description>Recent content in Projects on Home</description> + <generator>Hugo -- gohugo.io</generator> + <language>en-us</language> + <copyright>Jan Sebastian Götte</copyright> + <lastBuildDate>Wed, 04 Oct 2023 23:42:00 +0200</lastBuildDate><atom:link href="http://jaseg.de/projects/index.xml" rel="self" type="application/rss+xml" /> + <item> + <title>KiMesh</title> + <link>http://jaseg.de/projects/kimesh/</link> + <pubDate>Wed, 04 Oct 2023 23:42:00 +0200</pubDate> + + <guid>http://jaseg.de/projects/kimesh/</guid> + <description><div class="document"> + + +<p>KiMesh is a KiCad plugin that automatically creates security meshes with two or traces covering an arbitrarily-shaped outline on the board.</p> +</div></description> + </item> + + <item> + <title>Gerbolyze</title> + <link>http://jaseg.de/projects/gerbolyze/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + + <guid>http://jaseg.de/projects/gerbolyze/</guid> + <description><div class="document"> + + +<p>Gerbolyze is a tool that allows the modification of Gerber PCB artwork with a vector graphics editor like Inkscape. Gerbolyze directly converts between SVG and Gerber, and accurately reproduces details that other tools can not.</p> +</div></description> + </item> + + <item> + <title>Gerbonara</title> + <link>http://jaseg.de/projects/gerbonara/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + + <guid>http://jaseg.de/projects/gerbonara/</guid> + <description><div class="document"> + + +<p>Gerbonara is a user-friendly, powerful tool for reading, writing, modification and rendering of Gerber PCB artwork from the command line or from Python code. Gerbonara supports the Gerber dialects of all industry-standard EDA tools.</p> +</div></description> + </item> + + <item> + <title>lolcat-c</title> + <link>http://jaseg.de/projects/lolcat-c/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + + <guid>http://jaseg.de/projects/lolcat-c/</guid> + <description><div class="document"> + + +<p>lolcat-c is a small, high-performance re-implementation of the <a class="reference external" href="https://github.com/busyloop/lolcat">lolcat</a> rainbow cat utility. lolcat-c is meant as a lolcat that you can actually use in production. It is fast, not slowing down whatever you pipe through it, and it robustly handles real-world terminal output including escape sequences.</p> +</div></description> + </item> + + <item> + <title>python-mpv</title> + <link>http://jaseg.de/projects/python-mpv/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + + <guid>http://jaseg.de/projects/python-mpv/</guid> + <description><div class="document"> + + +<p>python-mpv is a small, ctypes-based Python library wrapping the libmpv media player library. Despite its small size and simple API, python-mpv allows advanced control over libmpv and beyond simple remote control of mpv can be used to embed mpv in OpenGL, Qt, and GTK-based Python applications.</p> +</div></description> + </item> + + <item> + <title>svg-flatten</title> + <link>http://jaseg.de/projects/svg-flatten/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + + <guid>http://jaseg.de/projects/svg-flatten/</guid> + <description><div class="document"> + + +<p>svg-flatten is a command-line utility that performs vector occlusion and clipping on SVG files, producing a flattened SVG file without overlapping elements, without changing what the file looks like. svg-flatten is used as a part of gerbolyze.</p> +</div></description> + </item> + + <item> + <title>wsdiff</title> + <link>http://jaseg.de/projects/wsdiff/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + + <guid>http://jaseg.de/projects/wsdiff/</guid> + <description><div class="document"> + + +<p>wsdiff is a command-line utility that produces self-contained, syntax-highlighted, HTML-formatted diffs that support both unified and side-by-side diffs from a single source file using nothing but CSS magic.</p> +</div></description> + </item> + + </channel> +</rss> diff --git a/projects/kimesh/index.html b/projects/kimesh/index.html new file mode 100644 index 0000000..37d06b2 --- /dev/null +++ b/projects/kimesh/index.html @@ -0,0 +1,104 @@ +<!DOCTYPE html> +<html><head> + <meta charset="utf-8"> + <title>KiMesh | Home</title> + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="mobile-web-app-capable" content="yes"> + <meta name="color-scheme" content="dark light"> + <link rel="stylesheet" href="/style.css"> +</head> +<body><nav> + <div class="internal"> + + <a href="/" title="Home">Home</a> + <a href="/blog/" title="Blog">Blog</a> + <a href="/projects/" title="Projects">Projects</a> + <a href="/about/" title="About">About</a> + </div> + <div class="external"> + <a href="https://git.jaseg.de/" title="cgit">cgit</a> + <a href="https://github.com/jaseg" title="Github">Github</a> + <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a> + <a href="https://chaos.social/jaseg" title="Mastodon">Mastodon</a> + </span> +</nav> + + <header> + <h1>KiMesh</h1> +<ul class="breadcrumbs"> + <li><a href="/">jaseg.de</a></li> + <li><a href="/projects/">Projects</a></li><li><a href="/projects/kimesh/">KiMesh</a></li> +</ul> + <strong>2023-10-04</strong> + </header> + <main> + <div class="links"> + <a href="https://git.jaseg.de/kimesh.git">Sources</a> + <a href="https://github.com/jaseg/kimesh/issues">Issues</a> + <a href="https://jaseg.de/projects/kimesh">Docs</a> + </div> + <div class="document" id="kicad-security-mesh-generator"> +<h1 class="title">KiCAD Security Mesh Generator</h1> + +<img alt="A screenshot of KiCAD showing a PCB security mesh generated by KiMesh." src="kicad-mesh-result-large.png" style="width: 800px;" /> +<p>This repository contains KiMesh, a KiCAD pcbnew plugin that generates security mesh traces on a KiCAD PCB.</p> +<div class="section" id="installation"> +<h2>Installation</h2> +<p>KiMesh has two parts: The pcbnew plugin that generates the traces, and the magic footprints that you use to tell the +plugin how many traces of which dimensions to generate where.</p> +<p>To install the plugin, copy the "kimesh" directory into your KiCAD installation's scripting plugin folder. Usually, this +is <cite>~/.config/kicad/scripting/plugins/</cite> for KiCAD stable installations or +<cite>~/.config/kicad/[major version].99/scripting/plugins/</cite> for nightly builds. On Windows, these paths can be found in your +user account's <cite>AppData/Roaming</cite> directory.</p> +<p>To install the footprint libraries, the easiest way is to download the library zip from the project's repo +<a class="reference external" href="https://git.jaseg.de/kimesh.git/plain/mesh_footprints.tar.xz?h=main">[link]</a>, unpack it to your project folder, and +add the unpacked libraries as project-specific libraries through KiCad's library management thingy.</p> +</div> +<div class="section" id="usage"> +<h2>Usage</h2> +<p>To work, KiMesh requires four things:</p> +<ol class="arabic simple"> +<li>An area free of other features such as footprints or traces where to generate the mesh.</li> +<li>One or more "graphic polygons" on a drawing layer that specify the area of the mesh.</li> +<li>A closed board outline on the <cite>Edge.Cuts</cite> layer.</li> +<li>One of the magic footprints from the KiMesh anchor library that defines the mesh's number of wires and their +dimensions, and tells KiMesh where to start the mesh and in which direction to start it.</li> +</ol> +<p>You can choose any layer for the outline polygons, such as the pre-defined <cite>User.Eco1</cite> or <cite>User.X</cite> layers, or you can +define your own. You just have to select that layer later in KiMesh's generator dialog. Note that KiMesh only processes +graphic polygons on that layer, and ignores other shapes such as lines, rectangles or circles. You can still use other +shapes, but you have to manually convert them to polygons before running KiMesh. To convert other shapes to +a polygon, select them, open the context menu with a right click, then choose the "Create from Selection 🞂 Create +Polygon from Selection" entry. For rectangles or circles, use the "Use Centerlines" option. For lines or arcs, use the +"Create bounding hull" option.</p> +<p>Place the magic anchor footprint on the outline of the mesh's shape polygons so that you have space to route out the +traces. The anchor footprint has an arrow on the <cite>F.Fab</cite> layer that indicates in which direction the mesh will be +generated.</p> +<p>I recommend adding the mesh to the schematic with one of KiCad's built-in <cite>Connector_02xN_Top_Bottom</cite> footprints. For a +mesh with k wires, choose a symbol with two rows of 2k pins each. For instance, for two mesh wires, choose +<cite>Connector_02x04_Top_bottom</cite>. Then assign one of the magic footprints to that symbol. To avoid DRC warnings, join the +two halves of the mesh on the output side of the footprint. That's the right side in default orientation, where the +higher-numbered pins are.</p> +<img alt="A screenshot of the connector footprint mentioned in the previous paragraph, shown conencted up as described in KiCad's schematic editor." src="screenshot-mesh-schematic.png" style="width: 800px;" /> +</div> +<div class="section" id="theory-of-operation"> +<h2>Theory of operation</h2> +<p>I have published a <a class="reference external" href="https://blog.jaseg.de/posts/kicad-mesh-plugin/">post</a> on my blog on the theory of operation behind KiMesh.</p> +</div> +</div> + </main><footer> + Copyright © 2023 Jan Sebastian Götte + / <a href="/about/">About</a> + / <a href="/imprint/">Imprint</a> +</footer> +<script> + if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser + contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to + prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition. + In support of an open web, this website does not function with this DRM. Please install a browser such + as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports + ad blockers.`; + </script> + </body> +</html> diff --git a/content/projects/kimesh/kicad-mesh-result-large.png b/projects/kimesh/kicad-mesh-result-large.png Binary files differindex 798287b..798287b 100644 --- a/content/projects/kimesh/kicad-mesh-result-large.png +++ b/projects/kimesh/kicad-mesh-result-large.png diff --git a/content/projects/kimesh/screenshot-mesh-schematic.png b/projects/kimesh/screenshot-mesh-schematic.png Binary files differindex 2869b5e..2869b5e 100644 --- a/content/projects/kimesh/screenshot-mesh-schematic.png +++ b/projects/kimesh/screenshot-mesh-schematic.png diff --git a/content/projects/lolcat-c/LOLCat-Rainbow.jpg b/projects/lolcat-c/LOLCat-Rainbow.jpg Binary files differindex 9524d26..9524d26 100644 --- a/content/projects/lolcat-c/LOLCat-Rainbow.jpg +++ b/projects/lolcat-c/LOLCat-Rainbow.jpg diff --git a/projects/lolcat-c/index.html b/projects/lolcat-c/index.html new file mode 100644 index 0000000..e4721da --- /dev/null +++ b/projects/lolcat-c/index.html @@ -0,0 +1,132 @@ +<!DOCTYPE html> +<html><head> + <meta charset="utf-8"> + <title>lolcat-c | Home</title> + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="mobile-web-app-capable" content="yes"> + <meta name="color-scheme" content="dark light"> + <link rel="stylesheet" href="/style.css"> +</head> +<body><nav> + <div class="internal"> + + <a href="/" title="Home">Home</a> + <a href="/blog/" title="Blog">Blog</a> + <a href="/projects/" title="Projects">Projects</a> + <a href="/about/" title="About">About</a> + </div> + <div class="external"> + <a href="https://git.jaseg.de/" title="cgit">cgit</a> + <a href="https://github.com/jaseg" title="Github">Github</a> + <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a> + <a href="https://chaos.social/jaseg" title="Mastodon">Mastodon</a> + </span> +</nav> + + <header> + <h1>lolcat-c</h1> +<ul class="breadcrumbs"> + <li><a href="/">jaseg.de</a></li> + <li><a href="/projects/">Projects</a></li><li><a href="/projects/lolcat-c/">lolcat-c</a></li> +</ul> + + </header> + <main> + <div class="links"> + <a href="https://git.jaseg.de/lolcat.git">Sources</a> + <a href="https://github.com/jaseg/lolcat">Github</a> + <a href="https://github.com/jaseg/lolcat/issues">Issues</a> + </div> + <div class="document"> + + +<div class="section" id="what"> +<h2>What?</h2> +<img alt="LOLCat-Rainbow.jpg" src="LOLCat-Rainbow.jpg" /> +</div> +<div class="section" id="screenshot"> +<h2>Screenshot</h2> +<img alt="screenshot.png" src="screenshot.png" /> +<img alt="sl.gif" src="sl.gif" /> +</div> +<div class="section" id="installation"> +<h2>Installation</h2> +<div class="section" id="archlinux"> +<h3>Archlinux</h3> +<p>There's an <a class="reference external" href="https://aur.archlinux.org/packages/c-lolcat">AUR package</a>:</p> +<pre class="code sh literal-block"> +<span class="lineno"></span><span class="line">$<span class="w"> </span>git<span class="w"> </span>clone<span class="w"> </span>https://aur.archlinux.org/packages/c-lolcat<span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span>$<span class="w"> </span><span class="nb">cd</span><span class="w"> </span>c-lolcat<span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span>$<span class="w"> </span>makepkg<span class="w"> </span>-csi +</span></pre> +</div> +<div class="section" id="fedora"> +<h3>Fedora</h3> +<pre class="code sh literal-block"> +<span class="lineno"></span><span class="line">$<span class="w"> </span>dnf<span class="w"> </span>install<span class="w"> </span>lolcat +</span></pre> +</div> +<div class="section" id="ubuntu-snap"> +<h3>Ubuntu (Snap)</h3> +<p>See <a class="reference external" href="https://blog.simos.info/how-to-make-a-snap-package-for-lolcat-with-snapcraft-on-ubuntu/">this awesome blog post by a kind person from the internet</a>:</p> +<pre class="code sh literal-block"> +<span class="lineno"></span><span class="line">$<span class="w"> </span>snap<span class="w"> </span>install<span class="w"> </span>lolcat-c +</span></pre> +</div> +<div class="section" id="mac"> +<h3>Mac</h3> +<p>Build loclcat with:</p> +<pre class="code sh literal-block"> +<span class="lineno"></span><span class="line">$<span class="w"> </span>make<span class="w"> </span>lolcat +</span></pre> +<p>...and put the resulting binary at a place of your choice.</p> +</div> +<div class="section" id="others"> +<h3>Others</h3> +<pre class="code sh literal-block"> +<span class="lineno"></span><span class="line">$<span class="w"> </span>make<span class="w"> </span><span class="o">&&</span><span class="w"> </span>sudo<span class="w"> </span>make<span class="w"> </span>install +</span></pre> +</div> +</div> +<div class="section" id="why"> +<h2>Why?</h2> +<p>This <cite>lolcat</cite> clone is an attempt to reduce the world's carbon dioxide emissions by optimizing inefficient code. It's +>10x as fast and <0.1% as large as the original one.</p> +<pre class="code sh literal-block"> +<span class="lineno"></span><span class="line">newton~/d/lolcat<span class="w"> </span><<span class="m">3</span><span class="w"> </span>dmesg>foo<span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span>newton~/d/lolcat<span class="w"> </span><<span class="m">3</span><span class="w"> </span><span class="nb">time</span><span class="w"> </span>upstream/bin/lolcat<span class="w"> </span>foo<span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="m">13</span>.51user<span class="w"> </span><span class="m">1</span>.34system<span class="w"> </span><span class="m">0</span>:15.99elapsed<span class="w"> </span><span class="m">92</span>%CPU<span class="w"> </span><span class="o">(</span>0avgtext+0avgdata<span class="w"> </span>10864maxresident<span class="o">)</span>k<span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span>0inputs+0outputs<span class="w"> </span><span class="o">(</span>0major+1716minor<span class="o">)</span>pagefaults<span class="w"> </span>0swaps<span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span>newton~/d/lolcat<span class="w"> </span><<span class="m">3</span><span class="w"> </span><span class="nb">time</span><span class="w"> </span>./lolcat<span class="w"> </span>foo<span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="m">0</span>.02user<span class="w"> </span><span class="m">0</span>.00system<span class="w"> </span><span class="m">0</span>:00.09elapsed<span class="w"> </span><span class="m">34</span>%CPU<span class="w"> </span><span class="o">(</span>0avgtext+0avgdata<span class="w"> </span>1936maxresident<span class="o">)</span>k<span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span>0inputs+0outputs<span class="w"> </span><span class="o">(</span>0major+117minor<span class="o">)</span>pagefaults<span class="w"> </span>0swaps +</span></pre> +<p>Bonus comparison with <a class="reference external" href="https://github.com/tehmaze/lolcat/">python-lolcat</a>:</p> +<pre class="code sh literal-block"> +<span class="lineno"></span><span class="line">newton~/d/lolcat<span class="w"> </span><<span class="m">3</span><span class="w"> </span>dmesg>foo<span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span>$<span class="w"> </span><span class="nb">time</span><span class="w"> </span>python-lolcat<span class="w"> </span>foo<span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="m">12</span>.27user<span class="w"> </span><span class="m">0</span>.00system<span class="w"> </span><span class="m">0</span>:12.29elapsed<span class="w"> </span><span class="m">99</span>%CPU<span class="w"> </span><span class="o">(</span>0avgtext+0avgdata<span class="w"> </span>11484maxresident<span class="o">)</span>k<span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span>0inputs+0outputs<span class="w"> </span><span class="o">(</span>0major+1627minor<span class="o">)</span>pagefaults<span class="w"> </span>0swaps<span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span>$<span class="w"> </span><span class="nb">time</span><span class="w"> </span>c-lolcat<span class="w"> </span>foo<span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="m">0</span>.29user<span class="w"> </span><span class="m">0</span>.00system<span class="w"> </span><span class="m">0</span>:00.30elapsed<span class="w"> </span><span class="m">98</span>%CPU<span class="w"> </span><span class="o">(</span>0avgtext+0avgdata<span class="w"> </span>468maxresident<span class="o">)</span>k<span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span>0inputs+0outputs<span class="w"> </span><span class="o">(</span>0major+21minor<span class="o">)</span>pagefaults<span class="w"> </span>0swaps +</span></pre> +<p>(Read: <cite>c-lolcat << python-lolcat << ruby-lolcat</cite>)</p> +</div> +</div> + </main><footer> + Copyright © 2023 Jan Sebastian Götte + / <a href="/about/">About</a> + / <a href="/imprint/">Imprint</a> +</footer> +<script> + if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser + contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to + prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition. + In support of an open web, this website does not function with this DRM. Please install a browser such + as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports + ad blockers.`; + </script> + </body> +</html> diff --git a/content/projects/lolcat-c/screenshot.png b/projects/lolcat-c/screenshot.png Binary files differindex 56282af..56282af 100644 --- a/content/projects/lolcat-c/screenshot.png +++ b/projects/lolcat-c/screenshot.png diff --git a/content/projects/lolcat-c/sl.gif b/projects/lolcat-c/sl.gif Binary files differindex 9d994e8..9d994e8 100644 --- a/content/projects/lolcat-c/sl.gif +++ b/projects/lolcat-c/sl.gif diff --git a/projects/python-mpv/index.html b/projects/python-mpv/index.html new file mode 100644 index 0000000..e7e19a7 --- /dev/null +++ b/projects/python-mpv/index.html @@ -0,0 +1,408 @@ +<!DOCTYPE html> +<html><head> + <meta charset="utf-8"> + <title>python-mpv | Home</title> + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="mobile-web-app-capable" content="yes"> + <meta name="color-scheme" content="dark light"> + <link rel="stylesheet" href="/style.css"> +</head> +<body><nav> + <div class="internal"> + + <a href="/" title="Home">Home</a> + <a href="/blog/" title="Blog">Blog</a> + <a href="/projects/" title="Projects">Projects</a> + <a href="/about/" title="About">About</a> + </div> + <div class="external"> + <a href="https://git.jaseg.de/" title="cgit">cgit</a> + <a href="https://github.com/jaseg" title="Github">Github</a> + <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a> + <a href="https://chaos.social/jaseg" title="Mastodon">Mastodon</a> + </span> +</nav> + + <header> + <h1>python-mpv</h1> +<ul class="breadcrumbs"> + <li><a href="/">jaseg.de</a></li> + <li><a href="/projects/">Projects</a></li><li><a href="/projects/python-mpv/">python-mpv</a></li> +</ul> + + </header> + <main> + <div class="links"> + <a href="https://git.jaseg.de/python-mpv.git">Sources</a> + <a href="https://github.com/jaseg/python-mpv/issues">Issues</a> + <a href="https://neinseg.gitlab.io/python-mpv">Docs</a> + <a href="https://pypi.org/project/mpv">PyPI</a> + </div> + <div class="document"> + + +<!-- vim: tw=120 sw=4 et --> +<p>python-mpv is a ctypes-based python interface to the mpv media player. It gives you more or less full control of all +features of the player, just as the lua interface does.</p> +<div class="section" id="installation"> +<h2>Installation</h2> +<pre class="code bash literal-block"> +<span class="lineno"></span><span class="line">pip<span class="w"> </span>install<span class="w"> </span>mpv +</span></pre> +<p>...though you can also realistically just copy <a class="reference external" href="https://raw.githubusercontent.com/jaseg/python-mpv/main/mpv.py">mpv.py</a> into your project as it's all nicely contained in one file.</p> +<div class="section" id="requirements"> +<h3>Requirements</h3> +<div class="section" id="libmpv"> +<h4>libmpv</h4> +<p><tt class="docutils literal">libmpv.so</tt> either locally (in your current working directory) or somewhere in your system library search path. This +module is somewhat lenient as far as <tt class="docutils literal">libmpv</tt> versions are concerned but since <tt class="docutils literal">libmpv</tt> is changing quite frequently +you'll only get all the newest features when using an up-to-date version of this module. The unit tests for this module +do some basic automatic version compatibility checks. If you discover anything missing here, please open an <a class="reference external" href="https://github.com/jaseg/python-mpv/issues">issue</a> or +submit a <a class="reference external" href="https://github.com/jaseg/python-mpv/pulls">pull request</a> on github.</p> +<p>On Windows you can place libmpv anywhere in your <tt class="docutils literal">%PATH%</tt> (e.g. next to <tt class="docutils literal">python.exe</tt>) or next to this module's +<tt class="docutils literal">mpv.py</tt>. Before falling back to looking in the mpv module's directory, python-mpv uses the DLL search order built +into ctypes, which is different to the one Windows uses internally. Consult <a class="reference external" href="https://stackoverflow.com/a/23805306">this stackoverflow post</a> for details.</p> +</div> +<div class="section" id="python-3-7-officially"> +<h4>Python >= 3.7 (officially)</h4> +<p>The <tt class="docutils literal">main</tt> branch officially only supports recent python releases (3.5 onwards), but there is the somewhat outdated +but functional <a class="reference external" href="https://github.com/jaseg/python-mpv/tree/py2compat">py2compat branch</a> providing Python 2 compatibility.</p> +</div> +<div class="section" id="supported-platforms"> +<h4>Supported Platforms</h4> +<p><strong>Linux</strong>, <strong>Windows</strong> and <strong>OSX</strong> all seem to work mostly fine. For some notes on the installation on Windows see +<a class="reference external" href="https://github.com/jaseg/python-mpv/issues/60#issuecomment-352719773">this comment</a>. Shared library handling is quite bad on windows, so expect some pain there. On OSX there seems to be +some bug int the event logic. See <a class="reference external" href="https://github.com/jaseg/python-mpv/issues/36">issue 36</a> and <a class="reference external" href="https://github.com/jaseg/python-mpv/issues/61">issue 61</a> for details. Creating a pyQT window and having mpv draw +into it seems to be a workaround (about 10loc), but in case you want this fixed please weigh in on the issue tracker +since right now there is not many OSX users.</p> +</div> +</div> +</div> +<div class="section" id="usage"> +<h2>Usage</h2> +<pre class="code python literal-block"> +<span class="lineno"></span><span class="line"><span class="kn">import</span> <span class="nn">mpv</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span> <span class="o">=</span> <span class="n">mpv</span><span class="o">.</span><span class="n">MPV</span><span class="p">(</span><span class="n">ytdl</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span><span class="o">.</span><span class="n">play</span><span class="p">(</span><span class="s1">'https://youtu.be/DOmdB7D-pUU'</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span><span class="o">.</span><span class="n">wait_for_playback</span><span class="p">()</span> +</span></pre> +<p>python-mpv mostly exposes mpv's built-in API to python, adding only some porcelain on top. Most "<a class="reference external" href="https://mpv.io/manual/master/#list-of-input-commands">input commands</a>" are mapped to methods of the MPV class. Check out these methods and their docstrings in <a class="reference external" href="https://github.com/jaseg/python-mpv/blob/main/mpv.py">the source</a> for things you can do. Additional controls and status information are exposed through <a class="reference external" href="https://mpv.io/manual/master/#properties">MPV properties</a>. These can be accessed like <tt class="docutils literal">player.metadata</tt>, <tt class="docutils literal">player.fullscreen</tt> and <tt class="docutils literal">player.loop_playlist</tt>.</p> +<div class="section" id="threading"> +<h3>Threading</h3> +<p>The <tt class="docutils literal">mpv</tt> module starts one thread for event handling, since MPV sends events that must be processed quickly. The +event queue has a fixed maxmimum size and some operations can cause a large number of events to be sent.</p> +<p>If you want to handle threading yourself, you can pass <tt class="docutils literal">start_event_thread=False</tt> to the <tt class="docutils literal">MPV</tt> constructor and +manually call the <tt class="docutils literal">MPV</tt> object's <tt class="docutils literal">_loop</tt> function. If you have some strong need to not use threads and use some +external event loop (such as asyncio) instead you can do that, too with some work. The API of the backend C <tt class="docutils literal">libmpv</tt> +has a function for producing a sort of event file descriptor for a handle. You can use that to produce a file descriptor +that can be passed to an event loop to tell it to wake up the python-mpv event handler on every incoming event.</p> +<p>All API functions are thread-safe. If one is not, please file an issue on github.</p> +</div> +<div class="section" id="advanced-usage"> +<h3>Advanced Usage</h3> +<div class="section" id="logging-properties-python-key-bindings-screenshots-and-youtube-dl"> +<h4>Logging, Properties, Python Key Bindings, Screenshots and youtube-dl</h4> +<pre class="code python literal-block"> +<span class="lineno"></span><span class="line"><span class="ch">#!/usr/bin/env python3</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">mpv</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">def</span> <span class="nf">my_log</span><span class="p">(</span><span class="n">loglevel</span><span class="p">,</span> <span class="n">component</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="nb">print</span><span class="p">(</span><span class="s1">'[</span><span class="si">{}</span><span class="s1">] </span><span class="si">{}</span><span class="s1">: </span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">loglevel</span><span class="p">,</span> <span class="n">component</span><span class="p">,</span> <span class="n">message</span><span class="p">))</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span> <span class="o">=</span> <span class="n">mpv</span><span class="o">.</span><span class="n">MPV</span><span class="p">(</span><span class="n">log_handler</span><span class="o">=</span><span class="n">my_log</span><span class="p">,</span> <span class="n">ytdl</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">input_default_bindings</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">input_vo_keyboard</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="c1"># Property access, these can be changed at runtime</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="nd">@player</span><span class="o">.</span><span class="n">property_observer</span><span class="p">(</span><span class="s1">'time-pos'</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">def</span> <span class="nf">time_observer</span><span class="p">(</span><span class="n">_name</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="c1"># Here, _value is either None if nothing is playing or a float containing</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="c1"># fractional seconds since the beginning of the file.</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="nb">print</span><span class="p">(</span><span class="s1">'Now playing at </span><span class="si">{:.2f}</span><span class="s1">s'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">value</span><span class="p">))</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span><span class="o">.</span><span class="n">fullscreen</span> <span class="o">=</span> <span class="kc">True</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span><span class="o">.</span><span class="n">loop_playlist</span> <span class="o">=</span> <span class="s1">'inf'</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="c1"># Option access, in general these require the core to reinitialize</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span><span class="p">[</span><span class="s1">'vo'</span><span class="p">]</span> <span class="o">=</span> <span class="s1">'gpu'</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="nd">@player</span><span class="o">.</span><span class="n">on_key_press</span><span class="p">(</span><span class="s1">'q'</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">def</span> <span class="nf">my_q_binding</span><span class="p">():</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="nb">print</span><span class="p">(</span><span class="s1">'THERE IS NO ESCAPE'</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="nd">@player</span><span class="o">.</span><span class="n">on_key_press</span><span class="p">(</span><span class="s1">'s'</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">def</span> <span class="nf">my_s_binding</span><span class="p">():</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">pillow_img</span> <span class="o">=</span> <span class="n">player</span><span class="o">.</span><span class="n">screenshot_raw</span><span class="p">()</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">pillow_img</span><span class="o">.</span><span class="n">save</span><span class="p">(</span><span class="s1">'screenshot.png'</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span><span class="o">.</span><span class="n">play</span><span class="p">(</span><span class="s1">'https://youtu.be/DLzxrzFCyOs'</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span><span class="o">.</span><span class="n">wait_for_playback</span><span class="p">()</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">del</span> <span class="n">player</span> +</span></pre> +</div> +<div class="section" id="skipping-silence-using-libav-filters"> +<h4>Skipping silence using libav filters</h4> +<p>The following code uses the libav silencedetect filter to skip silence at the beginning of a file. It works by loading +the filter, then parsing its output from mpv's log. Thanks to Sean DeNigris on github (#202) for the original code!</p> +<pre class="code python literal-block"> +<span class="lineno"></span><span class="line"><span class="ch">#!/usr/bin/env python3</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">sys</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">mpv</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">p</span> <span class="o">=</span> <span class="n">mpv</span><span class="o">.</span><span class="n">MPV</span><span class="p">()</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">p</span><span class="o">.</span><span class="n">play</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">def</span> <span class="nf">skip_silence</span><span class="p">():</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">p</span><span class="o">.</span><span class="n">set_loglevel</span><span class="p">(</span><span class="s1">'debug'</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">p</span><span class="o">.</span><span class="n">af</span> <span class="o">=</span> <span class="s1">'lavfi=[silencedetect=n=-20dB:d=1]'</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">p</span><span class="o">.</span><span class="n">speed</span> <span class="o">=</span> <span class="mi">100</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">def</span> <span class="nf">check</span><span class="p">(</span><span class="n">evt</span><span class="p">):</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">toks</span> <span class="o">=</span> <span class="n">evt</span><span class="p">[</span><span class="s1">'event'</span><span class="p">][</span><span class="s1">'text'</span><span class="p">]</span><span class="o">.</span><span class="n">split</span><span class="p">()</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">if</span> <span class="s1">'silence_end:'</span> <span class="ow">in</span> <span class="n">toks</span><span class="p">:</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">return</span> <span class="nb">float</span><span class="p">(</span><span class="n">toks</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">p</span><span class="o">.</span><span class="n">time_pos</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">wait_for_event</span><span class="p">(</span><span class="s1">'log_message'</span><span class="p">,</span> <span class="n">cond</span><span class="o">=</span><span class="n">check</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">p</span><span class="o">.</span><span class="n">speed</span> <span class="o">=</span> <span class="mi">1</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">p</span><span class="o">.</span><span class="n">af</span> <span class="o">=</span> <span class="s1">''</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">skip_silence</span><span class="p">()</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">p</span><span class="o">.</span><span class="n">wait_for_playback</span><span class="p">()</span> +</span></pre> +</div> +<div class="section" id="video-overlays"> +<h4>Video overlays</h4> +<pre class="code python literal-block"> +<span class="lineno"></span><span class="line"><span class="ch">#!/usr/bin/env python3</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">time</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">from</span> <span class="nn">PIL</span> <span class="kn">import</span> <span class="n">Image</span><span class="p">,</span> <span class="n">ImageDraw</span><span class="p">,</span> <span class="n">ImageFont</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">mpv</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span> <span class="o">=</span> <span class="n">mpv</span><span class="o">.</span><span class="n">MPV</span><span class="p">()</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span><span class="o">.</span><span class="n">loop</span> <span class="o">=</span> <span class="kc">True</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span><span class="o">.</span><span class="n">play</span><span class="p">(</span><span class="s1">'test.webm'</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span><span class="o">.</span><span class="n">wait_until_playing</span><span class="p">()</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">font</span> <span class="o">=</span> <span class="n">ImageFont</span><span class="o">.</span><span class="n">truetype</span><span class="p">(</span><span class="s1">'DejaVuSans.ttf'</span><span class="p">,</span> <span class="mi">40</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">while</span> <span class="ow">not</span> <span class="n">player</span><span class="o">.</span><span class="n">core_idle</span><span class="p">:</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.5</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">overlay</span> <span class="o">=</span> <span class="n">player</span><span class="o">.</span><span class="n">create_image_overlay</span><span class="p">()</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">for</span> <span class="n">pos</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">500</span><span class="p">,</span> <span class="mi">5</span><span class="p">):</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">ts</span> <span class="o">=</span> <span class="n">player</span><span class="o">.</span><span class="n">time_pos</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">if</span> <span class="n">ts</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">break</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">img</span> <span class="o">=</span> <span class="n">Image</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s1">'RGBA'</span><span class="p">,</span> <span class="p">(</span><span class="mi">400</span><span class="p">,</span> <span class="mi">150</span><span class="p">),</span> <span class="p">(</span><span class="mi">255</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">0</span><span class="p">))</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">d</span> <span class="o">=</span> <span class="n">ImageDraw</span><span class="o">.</span><span class="n">Draw</span><span class="p">(</span><span class="n">img</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">d</span><span class="o">.</span><span class="n">text</span><span class="p">((</span><span class="mi">10</span><span class="p">,</span> <span class="mi">10</span><span class="p">),</span> <span class="s1">'Hello World'</span><span class="p">,</span> <span class="n">font</span><span class="o">=</span><span class="n">font</span><span class="p">,</span> <span class="n">fill</span><span class="o">=</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">128</span><span class="p">))</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">d</span><span class="o">.</span><span class="n">text</span><span class="p">((</span><span class="mi">10</span><span class="p">,</span> <span class="mi">60</span><span class="p">),</span> <span class="sa">f</span><span class="s1">'t=</span><span class="si">{</span><span class="n">ts</span><span class="si">:</span><span class="s1">.3f</span><span class="si">}</span><span class="s1">'</span><span class="p">,</span> <span class="n">font</span><span class="o">=</span><span class="n">font</span><span class="p">,</span> <span class="n">fill</span><span class="o">=</span><span class="p">(</span><span class="mi">255</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">255</span><span class="p">))</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">overlay</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">img</span><span class="p">,</span> <span class="n">pos</span><span class="o">=</span><span class="p">(</span><span class="mi">2</span><span class="o">*</span><span class="n">pos</span><span class="p">,</span> <span class="n">pos</span><span class="p">))</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.05</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">overlay</span><span class="o">.</span><span class="n">remove</span><span class="p">()</span> +</span></pre> +</div> +<div class="section" id="playlist-handling"> +<h4>Playlist handling</h4> +<pre class="code python literal-block"> +<span class="lineno"></span><span class="line"><span class="ch">#!/usr/bin/env python3</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">mpv</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span> <span class="o">=</span> <span class="n">mpv</span><span class="o">.</span><span class="n">MPV</span><span class="p">(</span><span class="n">ytdl</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">input_default_bindings</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">input_vo_keyboard</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span><span class="o">.</span><span class="n">playlist_append</span><span class="p">(</span><span class="s1">'https://youtu.be/PHIGke6Yzh8'</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span><span class="o">.</span><span class="n">playlist_append</span><span class="p">(</span><span class="s1">'https://youtu.be/Ji9qSuQapFY'</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span><span class="o">.</span><span class="n">playlist_append</span><span class="p">(</span><span class="s1">'https://youtu.be/6f78_Tf4Tdk'</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span><span class="o">.</span><span class="n">playlist_pos</span> <span class="o">=</span> <span class="mi">0</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">while</span> <span class="kc">True</span><span class="p">:</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="c1"># To modify the playlist, use player.playlist_{append,clear,move,remove}. player.playlist is read-only</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="nb">print</span><span class="p">(</span><span class="n">player</span><span class="o">.</span><span class="n">playlist</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">player</span><span class="o">.</span><span class="n">wait_for_playback</span><span class="p">()</span> +</span></pre> +</div> +<div class="section" id="directly-feeding-mpv-data-from-python"> +<h4>Directly feeding mpv data from python</h4> +<pre class="code python literal-block"> +<span class="lineno"></span><span class="line"><span class="ch">#!/usr/bin/env python3</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">mpv</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span> <span class="o">=</span> <span class="n">mpv</span><span class="o">.</span><span class="n">MPV</span><span class="p">()</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="nd">@player</span><span class="o">.</span><span class="n">python_stream</span><span class="p">(</span><span class="s1">'foo'</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">def</span> <span class="nf">reader</span><span class="p">():</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s1">'test.webm'</span><span class="p">,</span> <span class="s1">'rb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">yield</span> <span class="n">f</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="mi">1024</span><span class="o">*</span><span class="mi">1024</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span><span class="o">.</span><span class="n">play</span><span class="p">(</span><span class="s1">'python://foo'</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span><span class="o">.</span><span class="n">wait_for_playback</span><span class="p">()</span> +</span></pre> +</div> +<div class="section" id="using-external-subtitles"> +<h4>Using external subtitles</h4> +<p>The easiest way to load custom subtitles from a file is to pass the <tt class="docutils literal"><span class="pre">--sub-file</span></tt> option to the <tt class="docutils literal">loadfile</tt> call:</p> +<pre class="code python literal-block"> +<span class="lineno"></span><span class="line"><span class="ch">#!/usr/bin/env python3</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">mpv</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span> <span class="o">=</span> <span class="n">mpv</span><span class="o">.</span><span class="n">MPV</span><span class="p">()</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span><span class="o">.</span><span class="n">loadfile</span><span class="p">(</span><span class="s1">'test.webm'</span><span class="p">,</span> <span class="n">sub_file</span><span class="o">=</span><span class="s1">'test.srt'</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span><span class="o">.</span><span class="n">wait_for_playback</span><span class="p">()</span> +</span></pre> +<p>Note that you can also pass many other options to <tt class="docutils literal">loadfile</tt>. See the mpv docs for details.</p> +<p>If you want to add subtitle files or streams at runtime, you can use the <tt class="docutils literal"><span class="pre">sub-add</span></tt> command. <tt class="docutils literal"><span class="pre">sub-add</span></tt> can only be +called once the player is done loading the file and starts playing. An easy way to wait for this is to wait for the +<tt class="docutils literal"><span class="pre">core-idle</span></tt> property.</p> +<pre class="code python literal-block"> +<span class="lineno"></span><span class="line"><span class="ch">#!/usr/bin/env python3</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">mpv</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span> <span class="o">=</span> <span class="n">mpv</span><span class="o">.</span><span class="n">MPV</span><span class="p">()</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span><span class="o">.</span><span class="n">play</span><span class="p">(</span><span class="s1">'test.webm'</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span><span class="o">.</span><span class="n">wait_until_playing</span><span class="p">()</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span><span class="o">.</span><span class="n">sub_add</span><span class="p">(</span><span class="s1">'test.srt'</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span><span class="o">.</span><span class="n">wait_for_playback</span><span class="p">()</span> +</span></pre> +</div> +<div class="section" id="using-mpv-s-built-in-gui"> +<h4>Using MPV's built-in GUI</h4> +<p>python-mpv is using mpv via libmpv. libmpv is meant for embedding into other applications and by default disables most +GUI features such as the OSD or keyboard input. To enable the built-in GUI, use the following options when initializing +the MPV instance. See <a class="reference external" href="https://github.com/jaseg/python-mpv/issues/61">Issue 102</a> for more details</p> +<pre class="code python literal-block"> +<span class="lineno"></span><span class="line"><span class="c1"># Enable the on-screen controller and keyboard shortcuts</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span> <span class="o">=</span> <span class="n">mpv</span><span class="o">.</span><span class="n">MPV</span><span class="p">(</span><span class="n">input_default_bindings</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">input_vo_keyboard</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">osc</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="c1"># Alternative version using the old "floating box" style on-screen controller</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span> <span class="o">=</span> <span class="n">mpv</span><span class="o">.</span><span class="n">MPV</span><span class="p">(</span><span class="n">player_operation_mode</span><span class="o">=</span><span class="s1">'pseudo-gui'</span><span class="p">,</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">script_opts</span><span class="o">=</span><span class="s1">'osc-layout=box,osc-seekbarstyle=bar,osc-deadzonesize=0,osc-minmousemove=3'</span><span class="p">,</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">input_default_bindings</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">input_vo_keyboard</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">osc</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> +</span></pre> +</div> +<div class="section" id="pyqt-embedding"> +<h4>PyQT embedding</h4> +<pre class="code python literal-block"> +<span class="lineno"></span><span class="line"><span class="ch">#!/usr/bin/env python3</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">mpv</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">sys</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">from</span> <span class="nn">PyQt5.QtWidgets</span> <span class="kn">import</span> <span class="o">*</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">from</span> <span class="nn">PyQt5.QtCore</span> <span class="kn">import</span> <span class="o">*</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">class</span> <span class="nc">Test</span><span class="p">(</span><span class="n">QMainWindow</span><span class="p">):</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">parent</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="n">parent</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">container</span> <span class="o">=</span> <span class="n">QWidget</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">setCentralWidget</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">container</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">container</span><span class="o">.</span><span class="n">setAttribute</span><span class="p">(</span><span class="n">Qt</span><span class="o">.</span><span class="n">WA_DontCreateNativeAncestors</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">container</span><span class="o">.</span><span class="n">setAttribute</span><span class="p">(</span><span class="n">Qt</span><span class="o">.</span><span class="n">WA_NativeWindow</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">player</span> <span class="o">=</span> <span class="n">mpv</span><span class="o">.</span><span class="n">MPV</span><span class="p">(</span><span class="n">wid</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">container</span><span class="o">.</span><span class="n">winId</span><span class="p">())),</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">vo</span><span class="o">=</span><span class="s1">'x11'</span><span class="p">,</span> <span class="c1"># You may not need this</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">log_handler</span><span class="o">=</span><span class="nb">print</span><span class="p">,</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">loglevel</span><span class="o">=</span><span class="s1">'debug'</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">player</span><span class="o">.</span><span class="n">play</span><span class="p">(</span><span class="s1">'test.webm'</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">app</span> <span class="o">=</span> <span class="n">QApplication</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="c1"># This is necessary since PyQT stomps over the locale settings needed by libmpv.</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="c1"># This needs to happen after importing PyQT before creating the first mpv.MPV instance.</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">locale</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">locale</span><span class="o">.</span><span class="n">setlocale</span><span class="p">(</span><span class="n">locale</span><span class="o">.</span><span class="n">LC_NUMERIC</span><span class="p">,</span> <span class="s1">'C'</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">win</span> <span class="o">=</span> <span class="n">Test</span><span class="p">()</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">win</span><span class="o">.</span><span class="n">show</span><span class="p">()</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">exec_</span><span class="p">())</span> +</span></pre> +</div> +<div class="section" id="pygobject-embedding"> +<h4>PyGObject embedding</h4> +<pre class="code python literal-block"> +<span class="lineno"></span><span class="line"><span class="ch">#!/usr/bin/env python3</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">gi</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">mpv</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">gi</span><span class="o">.</span><span class="n">require_version</span><span class="p">(</span><span class="s1">'Gtk'</span><span class="p">,</span> <span class="s1">'3.0'</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">from</span> <span class="nn">gi.repository</span> <span class="kn">import</span> <span class="n">Gtk</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">class</span> <span class="nc">MainClass</span><span class="p">(</span><span class="n">Gtk</span><span class="o">.</span><span class="n">Window</span><span class="p">):</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="nb">super</span><span class="p">(</span><span class="n">MainClass</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="fm">__init__</span><span class="p">()</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">set_default_size</span><span class="p">(</span><span class="mi">600</span><span class="p">,</span> <span class="mi">400</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="s2">"destroy"</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">on_destroy</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">widget</span> <span class="o">=</span> <span class="n">Gtk</span><span class="o">.</span><span class="n">Frame</span><span class="p">()</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">widget</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">show_all</span><span class="p">()</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="c1"># Must be created >after< the widget is shown, else property 'window' will be None</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">mpv</span> <span class="o">=</span> <span class="n">mpv</span><span class="o">.</span><span class="n">MPV</span><span class="p">(</span><span class="n">wid</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">widget</span><span class="o">.</span><span class="n">get_property</span><span class="p">(</span><span class="s2">"window"</span><span class="p">)</span><span class="o">.</span><span class="n">get_xid</span><span class="p">()))</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">mpv</span><span class="o">.</span><span class="n">play</span><span class="p">(</span><span class="s2">"test.webm"</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">def</span> <span class="nf">on_destroy</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">widget</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">mpv</span><span class="o">.</span><span class="n">terminate</span><span class="p">()</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">Gtk</span><span class="o">.</span><span class="n">main_quit</span><span class="p">()</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="c1"># This is necessary since like Qt, Gtk stomps over the locale settings needed by libmpv.</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="c1"># Like with Qt, this needs to happen after importing Gtk but before creating the first mpv.MPV instance.</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="kn">import</span> <span class="nn">locale</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">locale</span><span class="o">.</span><span class="n">setlocale</span><span class="p">(</span><span class="n">locale</span><span class="o">.</span><span class="n">LC_NUMERIC</span><span class="p">,</span> <span class="s1">'C'</span><span class="p">)</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">application</span> <span class="o">=</span> <span class="n">MainClass</span><span class="p">()</span><span class="w"></span></span> +<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">Gtk</span><span class="o">.</span><span class="n">main</span><span class="p">()</span> +</span></pre> +</div> +<div class="section" id="using-opengl-from-pygobject"> +<h4>Using OpenGL from PyGObject</h4> +<p>Just like it is possible to render into a GTK widget through X11 windows, it <a class="reference external" href="https://gist.github.com/jaseg/657e8ecca3267c0d82ec85d40f423caa">also is possible to render into a GTK +widget using OpenGL</a> through this python API.</p> +</div> +<div class="section" id="using-opengl-from-pyqt5-qml"> +<h4>Using OpenGL from PyQt5/QML</h4> +<p><a class="reference external" href="https://gitlab.com/robozman">Robozman</a> has mangaed to <a class="reference external" href="https://gitlab.com/robozman/python-mpv-qml-example">make mpv render into a PyQt5/QML widget using OpenGL</a> through this python API.</p> +</div> +<div class="section" id="using-mpv-inside-imgui-inside-opengl-via-glfw"> +<h4>Using mpv inside imgui inside OpenGL via GLFW</h4> +<p><a class="reference external" href="https://github.com/dfaker">dfaker</a> has written a demo (<a class="reference external" href="https://github.com/dfaker/imgui_glfw_pythonmpv_demo/blob/main/main.py">link</a>) that uses mpv to render video into an <a class="reference external" href="https://github.com/ocornut/imgui">imgui</a> UI running on an OpenGL context inside <a class="reference external" href="https://www.glfw.org/">GLFW</a>. Check out their demo to see how to integrate with imgui/OpenGL and how to access properties and manage the lifecycle of an MPV instance.</p> +</div> +</div> +</div> +<div class="section" id="running-tests"> +<h2>Running tests</h2> +<p>Use pytest to run tests.</p> +</div> +<div class="section" id="coding-conventions"> +<h2>Coding Conventions</h2> +<p>The general aim is <a class="reference external" href="https://www.python.org/dev/peps/pep-0008/">PEP 8</a>, with liberal application of the "consistency" section. 120 cells line width. Four spaces. +No tabs. Probably don't bother making pure-formatting PRs except if you think it <em>really</em> helps readability or it +<em>really</em> irks you if you don't.</p> +</div> +<div class="section" id="license"> +<h2>License</h2> +<p>python-mpv inherits the underlying libmpv's license, which can be either GPLv2 or later (default) or LGPLv2.1 or later. +For details, see <a class="reference external" href="https://github.com/mpv-player/mpv/blob/master/Copyright">the mpv copyright page</a>.</p> +</div> +</div> + </main><footer> + Copyright © 2023 Jan Sebastian Götte + / <a href="/about/">About</a> + / <a href="/imprint/">Imprint</a> +</footer> +<script> + if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser + contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to + prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition. + In support of an open web, this website does not function with this DRM. Please install a browser such + as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports + ad blockers.`; + </script> + </body> +</html> diff --git a/projects/svg-flatten/index.html b/projects/svg-flatten/index.html new file mode 100644 index 0000000..d64362d --- /dev/null +++ b/projects/svg-flatten/index.html @@ -0,0 +1,63 @@ +<!DOCTYPE html> +<html><head> + <meta charset="utf-8"> + <title>svg-flatten | Home</title> + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="mobile-web-app-capable" content="yes"> + <meta name="color-scheme" content="dark light"> + <link rel="stylesheet" href="/style.css"> +</head> +<body><nav> + <div class="internal"> + + <a href="/" title="Home">Home</a> + <a href="/blog/" title="Blog">Blog</a> + <a href="/projects/" title="Projects">Projects</a> + <a href="/about/" title="About">About</a> + </div> + <div class="external"> + <a href="https://git.jaseg.de/" title="cgit">cgit</a> + <a href="https://github.com/jaseg" title="Github">Github</a> + <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a> + <a href="https://chaos.social/jaseg" title="Mastodon">Mastodon</a> + </span> +</nav> + + <header> + <h1>svg-flatten</h1> +<ul class="breadcrumbs"> + <li><a href="/">jaseg.de</a></li> + <li><a href="/projects/">Projects</a></li><li><a href="/projects/svg-flatten/">svg-flatten</a></li> +</ul> + + </header> + <main> + <div class="links"> + <a href="https://git.jaseg.de/gerbolyze.git/tree/svg-flatten?h=main">Sources</a> + <a href="https://github.com/jaseg/gerbolyze/issues">Issues</a> + <a href="https://gerbolyze.gitlab.io/svg-flatten">Docs</a> + </div> + <div class="document"> + + +<p>svg-flatten is a command-line utility that performs vector occlusion and clipping on SVG files, producing a flattened +SVG file without overlapping elements, without changing what the file looks like. svg-flatten is used as a part of +gerbolyze.</p> +<p>I developed svg-flatten as part of <a class="reference external" href="http://jaseg.de/projects/gerbolyze/">gerbolyze</a>, but it can be used independently.</p> +</div> + </main><footer> + Copyright © 2023 Jan Sebastian Götte + / <a href="/about/">About</a> + / <a href="/imprint/">Imprint</a> +</footer> +<script> + if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser + contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to + prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition. + In support of an open web, this website does not function with this DRM. Please install a browser such + as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports + ad blockers.`; + </script> + </body> +</html> diff --git a/projects/wsdiff/index.html b/projects/wsdiff/index.html new file mode 100644 index 0000000..722cfb0 --- /dev/null +++ b/projects/wsdiff/index.html @@ -0,0 +1,105 @@ +<!DOCTYPE html> +<html><head> + <meta charset="utf-8"> + <title>wsdiff | Home</title> + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="mobile-web-app-capable" content="yes"> + <meta name="color-scheme" content="dark light"> + <link rel="stylesheet" href="/style.css"> +</head> +<body><nav> + <div class="internal"> + + <a href="/" title="Home">Home</a> + <a href="/blog/" title="Blog">Blog</a> + <a href="/projects/" title="Projects">Projects</a> + <a href="/about/" title="About">About</a> + </div> + <div class="external"> + <a href="https://git.jaseg.de/" title="cgit">cgit</a> + <a href="https://github.com/jaseg" title="Github">Github</a> + <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a> + <a href="https://chaos.social/jaseg" title="Mastodon">Mastodon</a> + </span> +</nav> + + <header> + <h1>wsdiff</h1> +<ul class="breadcrumbs"> + <li><a href="/">jaseg.de</a></li> + <li><a href="/projects/">Projects</a></li><li><a href="/projects/wsdiff/">wsdiff</a></li> +</ul> + + </header> + <main> + <div class="links"> + <a href="https://git.jaseg.de/wsdiff.git">Sources</a> + <a href="https://github.com/jaseg/wsdiff/issues">Issues</a> + <a href="https://pypi.org/project/wsdiff">PyPI</a> + </div> + <div class="document"> + + +<p>wsdiff is a python script that produces a diff of two files or directories as a single, self-contained HTML file. The +resulting diff works without Javascript and will automatically switch between inline and side-by-side formats depending +on available screen space.</p> +<div class="section" id="installation"> +<h2>Installation</h2> +<pre class="code sh literal-block"> +<span class="lineno"></span><span class="line">$<span class="w"> </span>pip<span class="w"> </span>install<span class="w"> </span>wsdiff +</span></pre> +</div> +<div class="section" id="usage"> +<h2>Usage</h2> +<pre class="literal-block"> +<span class="lineno"></span><span class="line">usage: wsdiff [-h] [-b] [-s SYNTAX_CSS] [-l LEXER] [-L] [-t PAGETITLE]</span> +<span class="lineno"></span><span class="line"> [-o OUTPUT] [--header] [--content]</span> +<span class="lineno"></span><span class="line"> [old] [new]</span> +<span class="lineno"></span><span class="line"></span> +<span class="lineno"></span><span class="line">Given two source files or directories this application creates an html page</span> +<span class="lineno"></span><span class="line">that highlights the differences between the two.</span> +<span class="lineno"></span><span class="line"></span> +<span class="lineno"></span><span class="line">positional arguments:</span> +<span class="lineno"></span><span class="line"> old source file or directory to compare ("before" file)</span> +<span class="lineno"></span><span class="line"> new source file or directory to compare ("after" file)</span> +<span class="lineno"></span><span class="line"></span> +<span class="lineno"></span><span class="line">options:</span> +<span class="lineno"></span><span class="line"> -h, --help show this help message and exit</span> +<span class="lineno"></span><span class="line"> -b, --open Open output file in a browser</span> +<span class="lineno"></span><span class="line"> -s SYNTAX_CSS, --syntax-css SYNTAX_CSS</span> +<span class="lineno"></span><span class="line"> Path to custom Pygments CSS file for code syntax</span> +<span class="lineno"></span><span class="line"> highlighting</span> +<span class="lineno"></span><span class="line"> -l LEXER, --lexer LEXER</span> +<span class="lineno"></span><span class="line"> Manually select pygments lexer (default: guess from</span> +<span class="lineno"></span><span class="line"> filename, use -L to list available lexers.)</span> +<span class="lineno"></span><span class="line"> -L, --list-lexers List available lexers for -l/--lexer</span> +<span class="lineno"></span><span class="line"> -t PAGETITLE, --pagetitle PAGETITLE</span> +<span class="lineno"></span><span class="line"> Override page title of output HTML file</span> +<span class="lineno"></span><span class="line"> -o OUTPUT, --output OUTPUT</span> +<span class="lineno"></span><span class="line"> Name of output file (default: stdout)</span> +<span class="lineno"></span><span class="line"> --header Only output HTML header with stylesheets and stuff,</span> +<span class="lineno"></span><span class="line"> and no diff</span> +<span class="lineno"></span><span class="line"> --content Only output HTML content, without header +</span></pre> +</div> +<div class="section" id="example-output"> +<h2>Example Output</h2> +<img alt="latest.png" src="latest.png" /> +</div> +</div> + </main><footer> + Copyright © 2023 Jan Sebastian Götte + / <a href="/about/">About</a> + / <a href="/imprint/">Imprint</a> +</footer> +<script> + if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser + contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to + prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition. + In support of an open web, this website does not function with this DRM. Please install a browser such + as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports + ad blockers.`; + </script> + </body> +</html> diff --git a/content/projects/wsdiff/latest.png b/projects/wsdiff/latest.png Binary files differindex 039fa46..039fa46 100644 --- a/content/projects/wsdiff/latest.png +++ b/projects/wsdiff/latest.png diff --git a/research/colorspace/Colorspace Visualization.ipynb b/research/colorspace/Colorspace Visualization.ipynb deleted file mode 100644 index c75dcdd..0000000 --- a/research/colorspace/Colorspace Visualization.ipynb +++ /dev/null @@ -1,69 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "from matplotlib import pyplot as plt\n", - "%matplotlib inline\n", - "from mpl_toolkits.mplot3d import Axes3D\n", - "from mpl_toolkits.mplot3d.art3d import Poly3DCollection\n", - "import json" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAArMAAAKhCAYAAABD+SaLAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzsvVtsXVl63/lba+99rrzqfqFUqtKlSlWqrotEVbed+GUG\nDtIP7YcBgtgw7GrHbgcwDGMCw2gYQZAESeYlQYC4jQQ9yEzmArfhOIPpsae77B7HDWecmlapuqsk\nUhRFUmJR4k28HV7OZd/Wmod99ubmES/nUIciJa0f0GiWeM7mIc/+zvrWt/7f/xNaawwGg8FgMBgM\nhucRud8vwGAwGAwGg8Fg2C0mmTUYDAaDwWAwPLeYZNZgMBgMBoPB8NxiklmDwWAwGAwGw3OLSWYN\nBoPBYDAYDM8tJpk1GAwGg8FgMDy3mGTWYDAYDAaDwfDcYpJZg8FgMBgMBsNzi0lmDQaDwWAwGAzP\nLXaLjzfjwgyGjYj9fgHbYOLVYNiIiVeD4fmh6Xg1lVmDwWAwGAwGw3OLSWYNBoPBYDAYDM8tJpk1\nGAwGg8FgMDy3mGTWYDAYDAaDwfDcYpJZg8FgMBgMBsNzi0lmDQaDwWAwGAzPLSaZNRgMBoPBYDA8\nt5hk1mAwGAwGg8Hw3GKSWYPBYDAYDAbDc4tJZg0Gg8FgMBgMzy0mmTUYDAaDwWAwPLfY+/0CXha0\n1gRBQLVaRUqJZVlkMhmklAhxkMeFGwwvJ3G8Akm8WpZl4tVgOIAopfA8D9d1sW0bx3GwbRspTc3u\nZcC8y88ApRTz8/N88cUXaK2Znp7m3r17rK6usrKyQrlcxnVdwjBEa73fL9dgeKnRWuP7Prdv30Zr\nTRiGfPzxx6ytrbG8vMzq6iq1Wo0gCEy8Ggz7TFwoGhkZYXV1Fa01n3/+OQsLC6ysrLCyskKlUsH3\nfZRSJmZfUExldg+JF0Lf96lWqzx8+JD79+8nu8ZyuUyxWCQIAnzfB0AIgWVZG3aVphJkMDwb4uqO\n1pq5uTnm5+exbRvP81haWqK7uxshBNVqNYnLdLyayq3B8OyIN55hGFIqlZicnEQpRRiGLCwscPTo\nUXK5HJ7n4XkeAFLKZA2O49XE7POPaHGXYrY0TaK1xvM8lFKUy2U+++wzMpkM77zzDtPT0ywsLGDb\nNmtra2QyGXp6eujt7aWjowMhBFprtNYIIbBtO/mfWSwPHAf5zTDx2iRxdScIAsIwZHh4mJmZGb7y\nla8AcPPmTY4ePcry8jJaa7q7u+np6aG7uxvbtjdUe+Lk1rIsLMsyx5wHCxOvLwjxxlMpxfT0NMPD\nw1y8eJEjR44wMDBAsVikUqngui4dHR309PTQ09NDLpfbEK9CiA2bUVNAOlA0/UaYyuweEFdjlVI8\nevSIqakpXnvtNZaXlxFCkM1m6ejo4LXXXgOgVqtRKpWYmppidXUVx3GS5LazszO5XhxgJrk1GNpH\neuO5srLCnTt3OHv2LMvLyziOg9Ya27a5cOECEGlpl5eXKZVKTExMoLWmq6uL3t7epHJbq9WS60sp\nn1gsDQbD7khvPIMg4M6dO1iWxfHjxykWi0m8nTp1imKxiNaatbU1lpaWGB0dpVqtbkhu8/l8orWF\nzePVrLEHH5PMtpF0kPm+z+DgILlcjuvXr7OyskKpVAJIKq8xuVyOEydOcOLECQBc16VUKjE7O8vI\nyAiWZdHb20tPT8+G5HZ+fp5cLkdPT4855jQYWiQtA9JaMz4+ztzcHO+88w7FYpGJiYlNn2fbNocP\nH+bw4cNAtHmNk9uHDx8ShuGG5NZxHFzXZXl5mcXFRfr6+jYcc5rk1mBoDqVUUihaWlri7t27nD9/\nnhMnTjA0NJSsq+k1VghBZ2cnnZ2dnD17NkluS6US9+/fp1KpUCwWk+S2UCgksoT79+9z/vz5DQUk\nk9weTEwy2ybSQbawsMC9e/e4dOkSR48eBWjp5s9msxw/fpzjx48D4HkepVKJx48fMzo6imVZ9PT0\nUK1WOXLkCEopo+EzGFogvfF0XZeBgQG6u7u5fv16y8mlZVkcOnSIQ4cOAVFyG29eJycnCYKAzs5O\n8vk8KysrnD592mj4DIYWSG88lVLcv3+fUqnE+++/Tz6f3/A4eLJglCad3J45cwatNeVymVKpxPj4\nOOVymUKhQE9PDwsLC5w/fx7f9zf0tZjk9uBhktmnJB1kYRgyOjpKpVLh2rVrZLPZ5HGNO8VWtMqZ\nTIZjx45x7NgxIEpul5eXWVhY4P79+zx69CjZVXZ1dT1xzJnW8Nm2bQLP8FKTbvKanZ3l/v37XL58\nOUlG06T1680Sn6T09vYmP29lZYXHjx+zvLzMzZs36ezsTCq3WzWoxP8zya3hZSbd5FWpVBgYGODY\nsWP09/dviIvdxogQgo6ODjo6Oujr60NrTaVSoVQq4Xken3zyCfl8PpH+bde0HevkTbw+e0wy+xTE\nFVEpJeVymYGBAU6dOsUbb7zxxM3cagK7HZlMhqNHj7K6ukpXVxfd3d3JEeaDBw8AkuQ2reGLf36j\n5tYccxpeBrTWSRwopbh79y5hGHL9+nUcx9mznyulpKenB9u2CYKAN954g9XVVUqlEsPDw3ieR0dH\nxxPJrdHwGV524pMTIQTT09NMTEzw1ltv0d3d/cRjn6Zg1HidYrFIsVhkamqKa9euUa1WExnR2toa\n2Wx2Q3IbhiFBECTXMH0tzx6TzO6SeGf2ox/9iNOnTzM9Pc2VK1fo7Ozc9PHtCrTNcByHI0eOcOTI\nkeS1lUolSqVS4m2b7r5WSuG6brJYBkGAbdsUi0WT3BpeSGIZ0Geffcbp06cZGxvjlVde4dSpU00t\nNO1cjKSUdHd3093dzSuvvIJSKtHwjYyM4LouxWIx0cmnk9v4dXieR09Pj0luDS8ksQzo4cOHVKtV\n1tbWsG2b69evY9ubpy2Na2y7EEJQKBQoFAqcOnUKIEluJycnWV1dfcKRqLFpu1ar0dXVZQav7CEm\nmW2RRq1dtVqlWq1y/fp1LMtq6hrtTmYbsW37ieQ23X2tlNqQ3M7NzaG1TgLVaPgMLwqNTV6VSoWx\nsbGkyesgIKWkq6uLrq6uJxpURkdHqdVqGxpUbNtmaGiId955BzAaPsOLRbr/pFKp8OjRIy5fvpw0\nSG9F47q6l2tsPp8nn89z8uRJYGdHouHhYS5fvkwmkwFMX8teYJLZFkhr7ebn5xkZGSGTyXD58uUd\nn7tXu8Zm2K77+tGjR1SrVfL5fLK7dBzHNKgYnnvSWrtarcbAwAAAV65cOTCJ7GZs1aCytLTE/fv3\nKZfL+L7P9PR00n1tGlQMzzuNTV5jY2PMzc1x8uTJHRPZRva6YNTITo5E1WqVyclJDh8+TGdnp2na\n3gNMMtsE6WqsUop79+5RrVa5du0an376aVPXeJa7xp1o7L6emJjA8zwqlQpTU1P4vk9XV1dSCcpm\nsxuSW2MybTjobNXk9ejRo+fuXk03qJw5cyZxXwCe6L7u6el5okEF2LAZNYul4aCR9nqOm7yOHz/O\nxYsXWVlZaeoaB2mNbXQkunnzJsVikbm5uQ2ORHHTtlJqy6ZtI/1rDpPM7kA6yNbW1hgcHOT06dNc\nvny5pQVhPyuzOyGl3KAHiruvS6USd+/exfM8Ojs7N0xQMQ0qhoNI4ySvoaEhlFJJk9fk5ORzP5s9\n7p7u6+tLuq+r1SpLS0tMTEywtraWdF/39PQkGj7ToGI4iKSrsVNTUzx8+DBp8oolcM3QDmeDvUII\nwbFjxxJZQuxIND8/z/379xFCbOtIZAav7IxJZregUWv38OFDpqenefvtt+no6Nj1NTf7er+Jx+bG\nxN3XPT09QJTcxt3X9+7d2zAesLe3d0PldmxsjAsXLphjTsMzJ621W15eZmho6Ikmr1aPH+PHHuR4\nTTeonD59OkluYxlRuvs6ndz6vs/U1BSHDx+mWCyaY07DM6VxyNCdO3fIZDIbmrxaide9bLJuN7Ej\nUexD7/v+jo5ErusyPz9PtVrlxIkTZvBKAyaZ3YR0kHmex+DgIMVikQ8++GDXN03j4nOQA62Rnbqv\n0w0qS0tLAEbDZ3hmNG48Hzx4wMLCAu+++y6FQmHDY6WUu1ocDxKNyWwj23Vfxw0qsT5+aWmJnp4e\no+EzPFPSG8/FxUWGh4e5cOFCciwfs9tk9qCxU8xu5ki0vLzM0tLSBkciKSVKqeTE2PS1rGOS2Qbi\nm6hQKCT6ltdffz25yXbLQZYZ7BRojWzXfe26Lp988skGa6F8Pm9Mpg17gtaapaUlcrlcoiXt7e2l\nv79/043nbhbHF2Fh2Kr7ulqtcufOnQ2V27hBxQxeMbSb2FEkCAIcx2F0dJSVlRWuXr1KLpd74vGt\nbD7j68PBS2xbXWMbm7bjvCTejJZKpQ2ORJlM5qVPbk0yWyeuxlYqFe7cuUNHRwe1Wo3+/v7ETuNp\nOEji9EZaDbRG4u7rjo4OZmZmuHbtWjIeMJ59XSgUkuS2UCgYDZ/hqYmbvOJhJRMTE7z55pvJ5K3N\nEEKglGrq+gf1HnzaeIX17uu5uTkuXLiAlHLTkdnbNaiYwSuGVoiriY8ePSIIAubm5jhx4gTXrl3b\n8n5uNV4P6hr7tMTJre/7dHd3c/r06Q2ORGEYJk3b3d3difQv9qZ+GZq2TTLLk752y8vLnDhxouUm\nr+1orPIcpEBrx+II0d8x/v22Gg8Yd1/n8/kkuY0nqKRNpuPqbS6XM8mtYQONTV7lcpn5+fmmJnnt\nRjN70O69dr4mpRRSyie6rz3Po1QqMT8/z9jYWKKj7+3tTZLb9OCVOJnN5/MmuTU8QdoNKD4+f//9\n9+nq6tr2ea2epGz29UGhHa8pjv1GR6IwDJOm7e0cidLJrRCCTCaD4zgvRHL7UiezjVq7iYkJZmZm\nKBQKnD17dk9+ZrO7zOcRrfWWR7vxeMDGBpWJiQnK5TLZbHZDcruyssL09DSXLl0CjIbPENHY5BUf\nkV++fLmpkbStambjDdpB2ny2k60S40wmw7Fjxzh27BgQaeBLpRILCwtJ93V3d3eS3AohuHnzJlev\nXgWePOY0ye3LSWOT1+DgIEEQ8Morr+yYyELryaxS6oWO2a3i1bIsent7k1OpZhyJRkZGOHz4MD09\nPS9EX8tLm8ymDdXjo8qOjg6uX7/OjRs39uRnVioVfvSjHyUi7rm5uWRIwX7SrkpPs9dpbFDRWlOr\n1VhaWkq6r+PFb21tjY6ODtOg8pLTaKg+Pj7OwsIC7733HsPDw3vSJBIEAZ9++mnSSTw5OUlvby/5\nfH5f77d2VmabvZbjOJt2Xy8tLSXd167rsrS0lCS3L7uG72Un7fW8sLDAvXv3uHjxYjI9sxlajdfJ\nyUkmJiYIwzDRknZ2dr4wm6k4Ud+JZhyJlFJYlkWhUCCbzT73TdsvZTKbDrLNmrzavaOr1WrcunUL\nrTVf/vKXqVQq3Lt3j5WVFSYmJpJOxbgyudXs6YPObhdZIUTSoBJ3X8/MzDA7O7thPGD899mpQcVU\ngl4s0l7P1WqVgYEBDh8+nDR5taqr2+mx8fSharXK+++/Tz6f58aNGyilkvGyHR0dGxocnyXt/HyK\nZQatsln39SeffMLy8vKG7utYwyelNINXXhLS1VitNSMjI6ytrSVNXjMzM22NV4D5+XkmJiY4evQo\nFy5cYGxsDKUU09PTDA8Pk8lkksplZ2fnc3uvaa2xLKvl523mSDQwMIBS6glHot7eXnK53HM3eOX5\nzJp2SeMkr+HhYTzPe+omr+2SuLm5uWRHGh/PxYnX+fPngY02HOPj44mBcm9vL93d3bu6edv1+lth\ntwvjZti2TWdnJ6+99hqwXvWZmZnh3r17yezrrZJbYzL9YpCWAU1PTzM+Pv5Ek1d80tEMO1V64o3n\noUOHkspOekhBPF52bW2NpaWlpMrR2dmZLJbZbPapf+9mfo920K7Yj+Ms/kyLR2YvLS3x8OHDpFLW\n2H1tBq+8WKQ3nuVymYGBAU6ePMnrr7+evJ+tSH12emy8wVxZWeGVV15JfobjOORyuUQD3njyl8vl\nkjW2o6PjubnX2hWvUkosy+LUqVMUi8UNjkRjY2NUKpUNyW0+nz/wTdsvTTKb1tqtrq4yODjImTNn\n6Ovre+pO/s1usHjHs7q6yrVr13Ach7GxsQ3PidnMhiOtT2tsvmh3cvusZQbN0JgYZ7PZTWdfb9V9\nrbVOGlTCMGR1dZWuri6Gh4f5yle+0pbXaNg70hvPIAgYGhpCCMEHH3zwxMlFu7xjHz9+zMjICJcv\nX+bQoUN8+umnT1j9xMfjnZ2ddHZ2cvbs2eQzZWlpiTt37iQdx3HMtsMNJc1+yAxavc5WDSpxQpHu\nvu7p6cGyrA2V24WFBY4fP86nn37KT//0Tz+3p1UvC439J48ePWJycpK3336bzs7ODY99GoeCNNVq\nlVu3bnHkyBGuXr3K1NRUcv80ksvlOHnyJCdPntwga4sn5sVuO729vRQKhX1PzLaiWZlBs9eK19j0\nZ1q8YY8diR48eJCMzG50JIqbttfW1shkMiwsLCRjt581L/wnRJzUBEGAlJIvvviCx48f86UvfWnX\nk7zSxFWhdOJVqVS4ffs2R48e5erVq0lANusza9v2hiM83/dZWlpKJBG2bdPT05Mcdx4U9jKZbaTZ\n7utYkxzLFf7Fv/gX/Mmf/ElbXqNhbwiCgFqthmVZlEolhoaGeO211xKP1EaednGMT2kqlcqGU5r4\nXt4pUU4f4Z07dy5pvlhaWmJycjKpSuZyuQPZAPosNrJbNagsLS1t2n09Pj7OoUOH+Af/4B/w8ccf\nP/XrM+wdceJjWVbS5JXL5fjggw82Lby04yRldnaW0dHRDac06cdulwQ3ytrSbjtx4hb7pMcDCg5K\ncrtVk/Vu2G6NbdWRaGFhgWKxyB/90R9x/vx5fvEXf7Etr7EVXuhkNm7yGhsbI5vNMjMzQ1dXF9ev\nX2/bDdEYNDMzM4yNje3od9mK7s1xnA2dxZ7nJcltbMXR09PDoUOH6OjoaPl3a7c1VztoVbKwVff1\n4uIii4uLuK7Lv/t3/45arcbKysqOnbS/8iu/wp/+6Z9y7NgxBgYGnvi+1prf+q3f4vd+7/dGgQrw\nodb6x638joaNxNWd+KirWCwmFj7b6VKf5tiyUqlw69Ytjh8/zhtvvPGEvc9uTNjTG6lXX301qUrO\nzs6ytLTEzZs3n0ojf5AW15hW43WrBpWlpSXu3r1LpVLh937v99BaMzk5yauvvrrjNZuJWSnlvwG+\nionZthD3n3zyySdcvHgxkdTFn8Ob0WospR8bhiHDw8NbesDHRaNWXRAa3XbK5TJLS0u4rsuNGzcS\njXx85L5fPMuCUZqdHIkWFxeZmpri008/pVAoEATBjp9r7Y7XF1ZIGPsghmFIpVJhbGyM1157jddf\nf72t+sk4aMIwZHBwkOnpaa5fv/5EItt4Az5NE0cmk+H48eMcOXKES5cucfnyZXK5HJOTk9y8eZPP\nP/+ciYkJVldXm/o57TxqfBa7xmaIu68vXrzIG2+8weHDh3n99deZm5vjb//tv82tW7e2ff6HH37I\nRx99tOX3v//97zMyMgJwEfgG8G93/WINidbO931c12VhYQEpJf39/TsuHrttAJuenuazzz7j8uXL\nvPrqq0/EQOOCuNuYjauSfX19HD58mPfee49Dhw5RKpX4/PPPuXnzJqOjoywsLBCG4Y7XexGS2Ubi\n6va5c+eSMcRf+cpXqFQq/OZv/ib/8l/+yx2v0UzMEsWridmnJC4UxWtsrVbjiy++4Nq1a9smsrD7\nymy5XObGjRsUi0Xee++9JxLZtEf50xBXJc+cOUM+n+f69eucPXuWIAi4d+8eN27cYGhoiOnp6Q19\nGs+C/UpmG4kdiU6dOsVbb73FsWPHePvtt5FS8oMf/ICf+7mf2/Ea7Y7XF64y22ioPjw8zOrqKufO\nnUs0qe0k1osMDw9z+vRpzpw5s+3NtheLUKOetFqtJo0Xsdg93lUWi8U9WwjbHWjt0smFYUg2m+Xd\nd99lenqab33rWzs+52d+5mcYHx/f8vvf/e53+aVf+iW+//3va+D/E0L0CCFOaq2n2/KiXyLSTZlx\nk1exWEyaiXai1cUxCAIGBgYIgoD+/v5trfGalQa1gmVZm2rkFxcXefDgAUKIpGr7LBpA20E7mz9j\nPvjgA3K5HH/yJ3/S1N+/mZgF/lcdvakmZndJuv8kbvKyLIt33323qc/s3Xg9T01NMT4+zpUrV7Y8\nVdvtSUozr6FRIx83gMYerl1dXcka226NfJp2n36281pnzpzh8OHD/PZv/zZ/42/8jR2f0+54faGS\n2XSQrayscOfOHc6ePduUOfNucV2XO3fu8KUvfanpn7PXhs6NeqA4uR0fH09Gy8bNKYVC4bloAHsa\nwjDEsqykQ7MdTE5ONorcHwGnAbMwNkljk9edO3ewLIv333+fwcHBpq/TyuLoui4PHz7k/PnzOzZ/\n7tXi2MhmGvm0/jutN42bG1+0yuxWtPN3nZycBHiY+icTsy3Q2OT18OFDpqenuXLlCkNDQy0nqM0Q\nhiFra2vJhL/tkuW9XldjpJR0dXXR1dWV2Fyl9d9BEGxIbtvpI9/O00+g7Sep7V5jaSFeX4hktjHI\n4iavd955h2KxyOTk5Aa/tGavud2HaLz4+r7P1atXn+jY3I5nFXTxz4oHFMRal0qlwtLSEvfv36da\nrSbJXiaTeSpD+HYuaO2+lmVZVKvVtgXaFu/fizdyZo9Iez3HFY64ySuu0jZLM4tj3GH96NEjTpw4\n0VS37X4ljI0DCuLmxtnZ2VjagpSS5eXlA2MIvxex3+6k3cTs7ollBfGQocHBQQqFAtevX0+s1JqN\n2WYfu7q6yu3bt7Ftm7fffnvHe2Ervfte06j/3sy5o1artWVI0kHcyMJ6wWg/19jnPplN+9q5rsvA\nwADd3d0bmrxaCTTY2m4rZmVlhYGBgWRXdhAWk2ZJC7njLsXBwUHCMHzCED42T26Wg1yZjXeNhUKh\nLdfs6+vj4cOHG/4JmGrLxV9g0tVYrTVjY2OUSqUNTV5Syqa0ozE7xXcsK7Btm4sXL1KtVpu67rOq\nzO5EY3Pj48ePmZqaShw60qOg98sQfi+S2Wq12tZmm76+PoD0LsbEbBOkN57z8/OMjIxw6dKlZLMF\nkXSmlWR2u1iKq76xtdft27ebvqfT8bpfziGNzh1hGHLjxo0NQ5LSPvKtyOnaKQ1oJ3EyGztBtINW\n4/W5TmbT1djZ2Vnu37+feESmaTWZ3cxuC6JAmZiYYGpqKrH2mp+fb3mR28+FsREhorF1J06cSI4w\n467i4eHhlgzhD3Iya9t2W5PZr33ta3zrW9/iF37hFwTwAbBstHfbk954VioVBgYGOHbsGP39/Rvu\nm1bf9+0WruXlZQYHBzl37hynTp1idna2pePQ9NcHJWZt26ajo4MLFy4AmxvCPwuNfJq9kgW1M5n9\n2te+xre//e1fEkL8ISZmdyS98VRKce/eParVKteuXXtiHdhtE2Yjvu8zMDBAJpNJqr7NcpBiNI1l\nWdi2vWFIUqlUSkZBC9H8kKR2ywzaRXoD2q5kttV4fS6T2c2avIIg4Pr165uW8HeTzDYGRRxk2Wx2\nQ5DtJoAO2s4qnYQKIZ7QA6UN4dN6oJ6eng1i9/205mrmWpVKJdEl7sTP//zP88Mf/pD5+Xn6+vr4\nJ//knyRSlb//9/8+X/3qV/ne974HEFtzfb0tL/YFpFEGND09zcTEBG+99Rbd3d1Pff3N4jWWG83M\nzCRyI2g9Xg/i4ti4aWw0hI8tc7744otnZgi/F8lsq1WeZmIWuI+J2R1J95+sra0xODjIqVOnuHz5\n8qb3T6uV2c0eWyqVGBwc5Pz580kz8+avTfNfb8Ff/QTGpgSeD64nkOIoHdlDBEoixVk68go/sHFs\nTUdOEITgWJpcBhBgC3AcjWODFJCxNdlMZPFkW5DLwcL8RRarkmy2PoLZ1uQyGtuKvs44CseW2PXr\nZLOajA22Dba9eZw1o5FPJ7fpuDqoMoP0Gttswajd8frcJbNKKRYWFlhcXKSnp4c7d+7wyiuvcOrU\nqS3f5N3IDNKPjxO5CxcuJCb96WvvZsE7iIvkZmxmCL+8vEypVEr0QLFnZjyYoh3sxeLYyrHld77z\nnW2/L4Tg93//9/n93//95lruX1Jird3t27e5dOkSd+7cwbbtHZs5WqFRluB5Hrdv3040fen7qFX/\nyYMgM2iFtEY+bQgfa+TjBo14M9qu32mvZAatJLPNxKzW+jee9vW9yMQbz9HRUQ4dOsTS0lLS5LVd\nX0irmtlGy7vx8XEeP37Me++9t2ky9GhG8b3/AgP3JQvLAq0g1BAEIADLAoTA1xZKg5ISjyjR9aXA\nVoJqVSOkoCMHbn1QWC4jUQqEAFuCVU8hhIZMBoLwOJPTmkJO4PoaCRTy4HlR0pvLCYJAIxE4NqCj\nxFhIsKVGCpBCYIkvM/HAQgjI50AFGsuCXBaUcpAiS0fXYfp/SiYa+Xi6pW3byWY0DMMDmcxCFF9h\nGDatCW53vD43yWy6uhMEAdPT08zOzm6oumzFbiuzWmsePHjA3Nzclsbtu9HmHLSFsZXdnpQyCazY\nED6ewT43N5e8R7s1hI/Zq0rPQZqY9qKT1totLi7yySef7Fh12Q1SymRm+OLiIkNDQ1sat7dqC5Tm\noMRsK/G6mUY+NoQfHR2lUqlw586dJF53e6y/F/G6tra2rwb1LxvpJq+1tTVmZmbo6elp6ri/VXu8\n+LGu63L79m06Ozvp7+9P7iHX0/z5X2s+/kxwf+J9vNAiCEBpyDqQy0DOBmyouhqhNVkHlBclkpbS\nBCpKOGUoCF0QocBSYCkQ9e/lLKj5ILUmmwfXBeqJrF+NklwpBH4tSlgFgAdSaQQCK9CoAISIKrJB\nGCXX2SwoX0TPtwBtoQKQUuCHOkrGhQI3ROkAyxZ01Q+pGjXyrusmm4q4Wfbw4cP09vbuWiPf7s+y\nvbAwbJXnIplNa+1qtRrDw8Mopfjggw+a+gDdTTJbq9UYGBh4IsgaOWiJ6bMmPYO9UCgkGtvYCizW\nA6VnsDddyGwjAAAgAElEQVTDXskM2qXnMWxNY5PX6OgoruvyN//m32ypobBZ4orA6Ogoi4uLXL16\ndcuf06q27yB8SDfyNJ83sSF8R0cHp06d4ic/+QlnzpxhaWmJe/futaSRT7MXDZvtbCYxbE964zk3\nN8fc3Bxnz55NdNk70UrTZrweLywscPfu3aSZbOCe5qO/0oyMS6bnBDVX4zgCx86QzWiyWYFS4Hka\nV9erpw7YOkpUC3lNJRBIBYWcwg8kQkM2A6GrsXSUWPpulNACBDXQAYQIREj0PwE5Aa7WSC3IOho/\nACk0jgWBK5BECawi+nlSgghABFH119Ear/511obA1yDBFj7K87FkPclVYFlRkvzK+c0/Y9I+8q7r\n8uqrr1Iulzdo5GNZQkdHR1OfVXshV9jvPOjAJ7PpRTFu8jp37hxzc3NNf3i2oueB6Jjy1q1bvPnm\nmztqLF80zezTXse27W0N4WMbk9gzc6vkdq9kBu1qADNsTlprl27yKhQKe5LIQvT+Pnr0iFOnTnHt\n2rVt75vdamYP2qa1nRP7Gg3h0xp53/ebMoTfCyu9djZsGjZnsyavWq3G6dOnW7KbbKVgpLWmVqtx\ne/ALxmf6+Y//T4axCVgrC/I5QS4TJahFJ5IR+J7EVgInA44NmYyI/r0KMqxXWWWUlMpAIy2NoyHw\no3/PCY0bgkCQzYLnakBg2+DXooRTak3oCiytEUpESWYYJayOFChfR9eyIumCFFGSG/gaSwosQHl1\nWUGoUb5AohFhAGEAQYAto2RZ2vXGawe0AurV28PHdo4fpRS5XI6urq5EIx83gE5MTDStkW+3C1Nj\ns+x+cGCT2cYmr7t376KU4vr16wRBwOzsbNPXanbXqJRidHSUcrnMl770paYmhrVybKm1Zmpqikwm\nc6AWxr0cmrCZ2D2WJMR6oHRyGwfYQWgoMTRPY5PX1NQUDx8+TJq8ZmdnW35Pm7kv5+bmGBsbo6en\nh4sXL+54zVY1sysrK5TL5ZaswvaavYzXzTTysWfm5OTkBo182jNzr9wMjCxo70hvPFdXVxkcHKSv\nr4/Lly8zPj7eVnu8mB9/XuV/+s4qYxM/RbWWI5+HjC1AQ16CX9UIX5DNQcYSZJ0o+QyqgloA+SxI\nS1NwQIXg16KE1QH8alQxlYEgqCmkAhRoXxAGgNIUM+CFIDUUshrXjZLhfFZEyS9gSQiqde2rgKAa\nVWhRGqx6QkpUNaYuWcjlo+QZFJlMCNUAB4W0iSQOErQE6YCuyxVUAKJey+nqbe7v3OhmIIR4YkhS\npVKhVCrx4MGDZM2Lk9vYR76d8Rp/nu6XFVrMgUxm00cey8vLDA0Nce7cOU6ePJksRu0OtGq1yq1b\nt5LEq1mtZ7PHlq7rUqlUqFQqlMtlKpUKP/7xjzdM9jmIlhutEFdUtsNxnA16IM/zWFpaYmZmhpGR\nERzHoaenB8/z2vq62m0bYlgnrbXzfZ87d+7gOM6GJq/4dKTZe3wnr2elFCMjI6yurnLp0iVWVlaa\num6zm0+tNaVSiTAMOXr0KKurq9y6dYve3l4OHTr01ObnB4Fm3o+0IXyskY+T29gzs7u7O3E5aQex\nzKDdPrOGiK0mecV2k7B7O8vtePz4Mb/7zzMsr3QCGsepUvWgCuQLoNwogQw8oCYIrOgI3rEDBA6h\nC7UQio4AS5NxNGiNpQQ5RxCECqHBcQTKV1iAtCTKh4ItkUiE0hQdgURgaSg4Aik0NpEWVyCwpSb5\niNAhEgtRb+YSAWSiwi7aBxHWM2Y3RHoBwomuFUrQ9UYw6g1mlhXlw0ICMvo3ISJd7ZlXm/fR3Wlq\nYayRj4ckxRr5sbGxZA3s6OhoW0Et3nzWarV9jdcDlcymq7EADx48YH5+nnfffXfDcVO7A212dpbR\n0VHefPNNent7uXPnzq50dVsRN6Vks1kuXryIUopSqcSVK1colUrMzMxw7969xPz80KFDz8wfEtpb\n6Wk1Ic9kMhw/fjxxiYjF7r7v8+Mf/zj5m7SiB2pkL8bZGiIam7yGh4e3dP2I/X6bYSuvZ4BKpcLt\n27c5duwYV69eZWlpqS0elzFxUwrA+fPn6enpoVwuc+bMGcIw3JDIxffmTv6Q7WQvK7M7sZkhfKlU\nYnx8nOXlZaanp5OTllY08mniTbE5SWk/6f4Tz/MYGBigo6Pjif4Ty7LaVjBSSjE8PMzKSoXllTeT\nf8864NdrFir+URryRQirOtKyao0VSCAEoclo8F2BD2SLIdQsQjSh9KHsoAHdGRCsRvedlQlASXwU\nECXAQkW/p5MPkV70uExnANXosynbEUK1ft9aIQ4SLQALsKMqrbQEvhViWQpLgvIklhCIUEeSBEsj\nLbAcUGFUeSYQCEdgWwrLiZJaIQWWozl5ZnP5TiOtxmxaI3/mzBm01qytrfH48WPK5TI3btygs7Mz\nidndSMHiz+lyubyvsqADk8w2NnkNDAzQ29u7afNVuwJNKcXdu3ep1Wr09/cnerBWO563emzaDeHq\n1av8+Mc/3vD9xq7FarXK0tISX3zxRXJjHDp0KDke2Cv2c3FsJBa7P3z4kP7+/uRv8vDhw10bwpvK\nbPvZrMlrZWVly+ardsXszMwMY2NjvPXWW8n4yN3abW1GrBW9dOkSpVJpw/PimexxIhfrwWN/yNhC\n59ChQ3s6iesgxatlWRw+fJjl5WW6urro6enZoJFvxRA+Jrb3qVarTftCG3YmXY2NZV6vv/76pn9j\nKWVLI+C3itdyuczt27c5ceIEd0YvAFGhSgiN763fe7ruAgDRMX/8SZHJhuDWE868gnryqaVG1WTy\nnNCVxFmCUuvxnSlowrXo62xRQa1+/0mF8NbzikjjWr+W0sR3qcyGCNeKLLfsEDuof0dpMkgQGicL\ngYrsEWxLQChACzSaQGu0UJFVV0agtUQpgdQhgecihE++aAN7k8w2IoRIPptc1+WNN95gbW0tcUnw\nPK8pjXyadLHopU5mtda4rovneTiOw8zMDA8ePEiqpJvR6pu5WaCVy2Vu3brFyZMnnzCC3m3Hc5rY\n67JYLG7rhpCmUfsSHw/EXcat3mTPmr0Ytdf4N4mT2/Hx8aRyEy+WW4nd44qx53kH8u/2vBFb92Qy\nmaTJ6/jx41y7dq1tXs+NyW+sm/c874nhKK3aAm0Wr2mvy9iGb3l5edsGsEY9eHyqMDk5yerqKrlc\nLtmMtvND/iB7wzZjCL+TtGovxk+/zMRrSVykiSc7pgs4jbS6+bQs6wlp2NTUFOPj44lu/p//m1ry\nvfSnhJOJdKj1V4tX0VBPLS2xntgGWuHU08xch0KX64OLMiGynmRqNEElleSm8nFpa+JPiVxRoyv1\ncffO+vNBI/3UkAKV+lqux53MKdACS0u0B5al0CjsDASBQkqBJSVaCaTUSKEIlQbhopQfCXcFCFtw\n+lxzjiHJ364Na2wcr/EGPT0kKZYRTU1NbRiS1Nvbu6m06qCcfO57MquUYnJyklqtRqVSQWu95SSv\n3dK4EDUGWSNPYwAN0QjNgYGBTY9bW3nN6eOBWKS/uLjI5ORk8vqEEPT29j7VEed+ygxaIW0IH+uB\n0obwceU17ZnZ+HsdNCeJ5414Q/GTn/yE06dP8+jRI65cubKjXvJpKrNra2vcvn2b06dPc+bMmU2b\nlp4mmY2HOuTz+aY3npuRttBJb7ziezObzRIEAbVa7amdHQ5KZTZmq8TYcRyOHj3K0aNHARJD+NnZ\nWUZGRjYYwnd2dibvpWkAax++7/P555/zyiuvMDIywpkzZ+jr69v2vW/Fait+fByDYRgyNDSUTOWM\npUX3RlMV04xGh6L+dUhQP9a3MwrCuHqqCd2U9EGl1jitoJ7Y5gsQrtYfkwtQtXruIBXaXU9sg5rY\nUL2N/93Ja3Ql+trOhwjPTp5v++s/U4brfy+lFcoW0SQxKZBaIlTkeoAELTTSUQQ+aBkiqKH9EGmD\nk5FoIomBFIKz51tLZtvBVvGa1sgDGzTy8ZCktNWm4zgbZAYvvWbWdV0ePnzI66+/zqlTp9p+/Tho\ngyBgaGgocUXYSr+3W5mB1pqJiQmmp6e3nGSyW9JdxnEjxtDQEKurq/zkJz/ZMMxgN81kB21xbIbN\nDOHX1tYolUqMjo5Sq9Xo6OjA933m5+dbuvZHH33Eb/3WbxGGIb/6q7/KN7/5zQ3fn5iY4Jd/+Zf5\n4Q9/+BOiT9Vvaq2/177f7mASn1qUy2VWV1f54IMPmtpI7UbnHltuTUxM8Pbbb29pFdSszGCtHPK/\nfLfK7Xu9HPmrBXJZCIOQwF2is+skh3vzfHJ3jWxGU8xLlpYcujoUvd0uc/MZqkHAoW6PXA6yGYts\nRpPLyU1jbbON1+PHj5mYmODu3buJ5dVumskOkswgptkq71aG8FNTU6yurpLNZvF9HyllYjPUDCZe\nt8d1Xe7du8c777zT1AahVTvLOF5XV1e5ffs2Z8+e5fTp08n9dXdUsV64jYYcxBFbq+okEcnlwCvX\nHyVC0NF37IxChrL+bI2qWZtWX4WVSpiLIVSjuLKyakP1lZTEIPRJklyR+iiz8gpRq78yO8QK15/v\naAu0ws5oQq3REiwpQEUNZQof3wvQOkDoqKorZKSd1VpHpWkB+Q5rXxq/m43XzTTy8ZCkuG8gl8sR\nhiFzc3MtVWabidlXXnnlL4EemojZfU9mV1ZWmJ6e5tixY3uSyMaEYciNGzeeCLLNaFVmoJQiCAIG\nBgZwHIf+/v4tF/h2HRFalkWhUKCrq4sjR45scAVotZmsXa/pWSezjcR6oM7OzkTsHntmfvOb32Ri\nYoJvfOMbfOMb36C/v3/L64RhyG/8xm/wgx/8gL6+Pvr7+/na177Gm2+uNy/8s3/2z/g7f+fv8Jd/\n+ZfvCSHeBL4HnNvzX3Kf0Vrz+eef4zgOb731VtPPa7UyCyT38U6jb3dKlP/fmxX+0w+qjE9Ger1q\nLcfElIsjBVpDV0cP1TFFLuviewopIJcRhCpLMa9x3QqO1YPWCiFWyWcFnqfJ5QS1msa26tUZoSkW\nLIJAUchLwkBhW5H1kBCQzzrU3LN0dWRACwQhlgjwg1m6u9f40nvOM20ma+dJym4lC+lqNkCtVmNw\ncJA/+7M/4/vf/z4jIyN8/etf58MPP9zyGiZet2dkZAStNVeuXGm60t1qvAohEjnJZhvPP/7uCuCi\nQpCWQCmN48ioCUpDEAqkhCAQZApRpieEJvADNGBnwLEFSoVYtkYoCxBYQiAFkIsMW7VSyEwIGiwZ\noiwbrSBfCPFXovszavCKPk+EpVNJLlihTJLs9Epm5zTEKolMiEBGyXUYGdKGIiCTUfiejwwVNlHx\nWFiRHVdkJktkYZD8zeDE2f2Rve02XtNDkiAqEE5MTDAxMcE//If/kGq1Snd3N7/zO7+z6al3TLMx\nC/yR1vrfNhOz+57MdnV1cfnyZR4/frwn14+tR6rVKj/1Uz/VVDC3WpmtVqvcuHGDc+fObZuQ76Xx\neqMrQLqZbG1tjWKxuGUzWbuS0L3QzD4NcXKbyWT49re/zc/8zM/w9/7e39vRDPzGjRtcuHCB1157\nDYC/+3f/Lt/97nc3BFrsQVqnG5jam9/iYCGEoL+/n48//ril57VSmV1ZWUkmEDXjHbtZvC4th/zP\nf7zKjVseaxUVNWYAltRIpck74PvRfe/VVNTQIaLu6ZwjCOpJbTwG07Y0vi8QNriVyGtHhGBpTd62\ncL1oZrpXCQmVJpQCt6ZRGYEbRNcKsoIgtJEBuG6IlAopo+POs68cpbtbN9VMdhDjtV3621wuRzab\n5Zd/+Zf5+OOP+fVf//Udde4mXrfn4sWLieNIs7QSr77v8+DBAzzP4ytf+cqmG7H/esNFBdH1LBFV\nQ0NfRc4FbpQ4agl+GeJCaz5bn6oFaCukthZdt9CjCFaje83uUvj1r0VGoT1JlHXqaBqXjjLQylqI\nCCQIsEKF1hKIpob5tWizmc0JvECDFGQzksBTYAkcW+AFIdKykDLSx/oyxEJj+RrhgWVDqBWWDdIW\nWLZImtq0AiE1tiWwnMgXFxE5Irx6cW8GyexEu+LVtm06Ojo4d+4cv/u7v8vdu3e5fv36jnKDZmMW\niPVrO8bsviezcdPAbkzJd/pQ932fwcFBHMdJvNWafU3NBvLy8jILCwtcu3Ztx+vv5J3ZTjZrJltc\nXNy0maydldl2BEg7F9n47+15Hrlcji9/+cs7PmdycpIzZ84k/93X18ePfvSjDY/5x//4H/OzP/uz\nfPOb33wEFIH/ti0v+Dlgtzv6nWI8lulMTU1x7NixZPe/E+mTlD/7qzLf/fMKE1MhQkQLhwWgNCqM\njgRR0QdfEEYV2MBVSAnKV4gQspZAuRq7bniuRTRdSClFwbaoVTWWVPhVgdBgo/FVNI899CM/SuUp\nhNLkLIHra2wBgauj48zAR4QeWQfCMKreXO3vprvbbqqZrBk/52bYD5lBM6Qn9r355pucPXt228eb\neN0ey7J21dDVzOOXl5cZHBzk2LFjuK676X1ZrSkWFtbX04wTjZSF+v1f//dCURBUorUok9eo2O1A\nKESqKStY7yNDiHW5gpPTiZQhU1SRCwFR9VV4MhpVoEG76/rXwA+RQb1KmwsQngVotO1jezL6OtA4\nAkS9Fc3KBghbIx1BKKLPDiEiiQF+3eklVGgZ2W9lMhKtIAwEQmrC0EcLj0JG4mR2Hsy0F+zVkJPD\nhw/zcz/3czs+p9mY/fa3v/2LQojfpImYPRAu/bs5gtzpOaVSiRs3bnDixAneeuutlr3ZdkpmwzBk\nYGCAcrnM2bNnm0qU96tqGTeTnT17lnfeeYdr165x4sSJpBN9dXWVBw8esLCw8FSTjg6ili8daM2K\n0zdL7htfz3e+8x0+/PBDtNZ9wFeB/00IcSDiaa/ZzXuzU7x6nsdnn31GuVzmgw8+IJvNNr2hnFtQ\n/O/f7eS/+8YM//p/XGHiYYAOgLrnY+AplK+x0KggmtGug+h4Mp+Jxk5mbYFX1UgNygcUZGyIHHc0\noQ8W0dhKS2gKOQkKpNb4bpQEZ0Q0pz0rBaEbXUsHGqEiT01CD1QF360hhUqM2bM5SXf3xrpCfPx+\n+fJl+vv7uXDhAkII7t+/zxdffMHU1BTT09O4rtvyexFzUJPZtJVeM5pZE687007fWFi3nRwaGuLd\nd9/l2LFjWz7+//jTSvK1kOuJLLBuVQBYYv35Sq1/w86F0Y4SwFbgr2tnw8r6++ynzBQse/2eyBbj\nmVtgZxUy1r4KvcGeK/TXn7PuewB2TiXPFxmFhcQKrMhvtv5oKwvKUiirXohREqmiBDpUCi0VWgRo\nXUaFFQQhx/v2r/N/r5LZZjXuzcYs8B+ajdl9r8wCu6rMbhVsscXO7OzsrpuwdvLYi229Tp8+TW9v\nb0vTqvZbVwpPTvW5efMmvb29iTfkbpvJ2lVR3e9Rtn19fTx8+DD570ePHj0hH/n3//7f89FHHwGg\ntf5YCJEDjgB7o5c5YLQ6EnG7xTH2dk27fzRzOvLHf7rK9/6iwuSMIgjyZDIaoSEMNFpplATbBscS\nSEIIwVICx1F4nsBCoDyFDCFvQUVrJIJapT7vMhTIEArZSDJgSaIxmSHYWuArRS4rCPyo4qpDhdCa\njC1xA4Wtwa8FSHwIQKKwJcnCrOsORKf6tu9mbmwmm5iYIAgCPM9jaGho181ke5GAtoNWx9maeN2Z\ndlZm07aT169fT5r1torXP//Pm1ty5XLRiQdEiWmtvP59R4oknQyVSHxf80US39hMPpWMio2+s4Er\nNvWdtXMaHT+/qKFWlyhYGplyLhBBKslVOrmWthSh1PUJYiJKWAWI+GcIsLIQeCpyK0ACPkrVsCyN\nCiMXAyEExZ5qUmR51jlBu2Pftm0qlUriWrITzcYs8EfQXMzuezIrhGhbZXazIEv/nGbfwO20rbFp\n+5UrV+ju7mZ6erpthu37hRCCQ4cObbDP2U0zWTtlBu1eZFvZNfb39zMyMsKDBw84ffo0f/iHf8gf\n/MEfbHjM2bNn+Yu/+As+/PBDhBCXgRww15YX/RwQx1+z75NlWU9UEbXW3L9/n/n5+cTbNWarZHZ2\nzuPffHuZzwYDXFdhSYHSGsLIhxId6ddsS5CxovGYOvSwbEGogFCRcTReIMhkJX5VIy2BDjS2VBSy\nFr6vcSwgDMnakLEshBPiOAIpJMIGqUKKWYFjA5ZASJBCkMmCRUg+E2CJoB7vmjCIXotlgRYCJyOi\n0ZYC3n2vdfupfD7PyZMnE2/Ixg7jZiaTtbsy2+5r+b7flC+0idedaVcyG0+zvHjxYuJIAdtvPkcf\nrBeGujqgUncrQATEKUg2pyBOJqVCpSqmjk55vaZekpONDjsAMh0ar66dbXQu0DWRJLmhmzqOFor4\nvzIFBZX61LCcwkpkDXp9UALRHhdA2JpARFp4KSN9rCUkQkZyJoQmFD624xP6YfT5YMvExcDJSOwM\nietOZ2dnssY+Cy/0OAFtB2lZULOnn83G7J07d/4b4D80E7P7nsxCFDjxCNtmafTBW1hY4O7du1y6\ndGnT3cF24zG3emyaeCRftVrd4IPb6vShg0jjorZVM1l6UMFmzWTtbEzZiypPs5VZ27b51re+xd/6\nW3+LMAz5lV/5Fd566y3+0T/6R1y7do2vfe1r/Kt/9a/4tV/7Nb7+9a9/TjSs8EN9EHcqe0S82DVb\nBWyMKdd1uXXrFt3d3Zt6u27lc/k//Ot5/st/LWNZ64tSNgvK12TzEsKo+cp2BK4GYfug7CiJDAER\nHQcSSjLCouxFBudlX4GWZC2FVw6xCxaeGyKloBqGqACcoqDq+mQzVtTUBZCTBJ6mULCo1DwcK0AQ\nTfzJZiyU0tiZ6KhRyKjKhFBoZaNUgJMNdqzMbkY6ztInKdD8ZLJ2JrPtdEYQQiSvq5nXZ+J1e+KC\nUSsTvRrXNa01Y2NjLC4ubjrlb6tk9tZgGSk8hAjJZAVaC7J5getpXF+RsRWOI8lmNcrS+IFGCYUS\nFrYlcJxooIKQ0bWVR2R/oAU6JQuQqYEGhaLGq/f65ToUqu5hKy2NTFVcRYNzQfy1dHTShSbyAcKv\nr/W2xg6jWqvlaEKiExw7K1ChAqmxLEXgu6B8pC0IQxHtYmX9B4hIanH0VIEzZ45v8JBfWlpicHCQ\nIAjo7u5OTlripLOdt2u719jYZ7bZvqRmY/ajjz76NSHEf08TMXsgktlW3ANiYh88pRRjY2OUSqUt\nR2nGP6OVQQjpx1arVW7dusWxY8d44403nlhIWklmD2JCu9Oi1mwzWRiGBzaZbdXQ+atf/Spf/epX\nN/zbP/2n/zT5+s033+Sv//qvAd5pywt9jogXx1Y2oOlKz/z8PMPDw1uO0oSt4/Wz29X69darNNH/\nCzIZQW0tmr/u16KYdIRE+5pMV6TV00ITVuo62EyA8MApRNUbCHFVgAgFMq+QbtSFrFwQCAKpkK5E\nyhA7ehnRtB8lUbaLpRUZy4pshoiOJ5E6qgr7YNuRzlZbCqUrKB1w9Fjrurmd4rXZyWTtiteYdibG\nra4HJl63x7IsarXazg+sk34va7Uat2/fpqenh/7+/k3f563i9Tv/qUS1EmlmVQiBiB+vsaUgDCCs\nAe56POc7BGFV4wNO93rDV6ZDE1brmxxb45VB1y28VpfrmSKCSk2jhQAhyIi44Cvo6BD1zwVBsSDx\nfQWOIJu18PwQ4QhsW+L5IZZtIYRA6gBhR5pZJxuiBNHYWhssHYIMAI0kQNS9ZpXWkQOXjvTyViba\nwFpOlG5JS3D+cs+Gv13sIX/u3LnEy3VxcZHx8fFkMFI8yKAdtPv0M67MtiLrbCZmtdY/3ez1DkQy\nu9uGkkqlwt27dzl06NC2ozRh91OC5ubmuHfv3pbjdVutzLbzJtoP0pPJzp49u2H83fLyMrdv306q\nQD09Pbvqut6rZpL9HLX3orFbU/V79+6xsrLCtWvXyGa3rkhuVkmaXwhYXY1+ZsYRuKGmUBBJ4upV\no9Kr0gECG8vWaL9ehVTRIpYrClQ10ui5FQCB4wi8GlgZnay2vhs9PlvQhJWorKK8+nGliL5n53TS\nZR25GIhoMJHQODlJEES2W4EXNaBYlsJTLo6lCAONsARXvtT6AtVqRXWryWSPHz/GdV3K5XISs9u9\nJzu9pr3gIG7+n0d2I+WD9fXvjTfe4PDhrTvvt/o8uPmTcvK1bZM0fGWyoOPqZ2pjKgQob/1e8moh\nVl0xa9nr/WL5DghXo3i08tRjlEgHUCFpzPKJhy1oAjtE1qJrqayPrMZ6WQ9R/1rZGkeDINqoWxmF\nUNHXAT6WpdEWuAoEOnIy8EXUTKYABKGKZAW5rI0KIw2/ZQuCwEeLgIwM6erZ/rMv7eXq+34Sr+Vy\nmc8++yyJ146Ojl3FyH6ffu4FByKZ3Q21Wo3h4WHefvvtpix8Wq3MhmHIyMgIy8vL286wbmXAAuzd\nh/7TsttFI91Mtry8zOuvv06lUnmqZrIXMdBeNFpdHIMgYGZmhrNnz3L16tUd77fN4vUP/ri0fr36\nEWN8vCisEFXvUnakRQA4eUFQAYTGr0R1G9sCD7BzENbqiWu1nujmBd5q1AyivHWPSACnoKBu9RPU\nogYQ24n8MoVdtweyNApVf2JUnbUsCFQAwiUIAK2RQhISnT6+/mb7qi3NkG4mE0Ik4yljLaTneRuO\nONs5VryV13hQPyefV1qNV6UUtVqNiYmJbde/mM3idW0tYGlp/Wc6tsAPo/c1CNebujqKCq9cTyZl\niK7HsbQ1Vpiy5KqsuxKoIK7Ebkxys4UQ6lO7ZGpqGEJDajRu6Onk5yu93uBl53SS2Fq5lCxBapzQ\nQggVxT0hyKjqGqrInksKgfIiuY2QGqXr9lxCImRIGFaRNhw60VrMO46TWBVWKhXeeOONRB8fe8jH\na2yzzWR7ITPY7zX2uUtmY+3q2toaFy9ebNqLspVkNgxDZmdn6evr23HhbbUyW6vVXtgPaq01tm1z\n+GprllQAACAASURBVPDhZBe/m2ay/bYNMWxPqzKD2dlZ7t27R2dnJ+fPn2/qOZvF6w//OmpDzuch\nqDd++F69gppRhJ6FlJog9rAMogaPQqfAXwPQhC6AIJcTlGtRE0lcvY3bpwsdAm+NqEO67nVpWdGC\n6eTWR2H6bpTUOjkIw2iiURhG/xYqRSg8HCuEIGoeU0E00lJHf0QOHdmdYXo7LfCklMnUvKdpJmt3\n85fnec+kEeZloNUm60qlwu3btxFC8P777zf13m62Dv7H/2t98yllfNoR4QiNihPTMNWvkQXq8RtN\n3YpiTWbVusOA0KjqelOXV11PbNM4WYWuV2yzRaAuUZC2RqZ8a/HXnxsGqu5AEA1qoO5BK3MarVQk\nHwjB0jLymkVEPtRCY2WjQQuy7mKgQwFWgKZGGITROFvgwuXj2/8xtyCOjVwux8mTJzl58iRa66R4\n1Eoz2V7IDEwyy/oH4U4f0pVKhVu3bnHixAlOnTrV0pvRbDK7uLjI3bt36ezs5MKFC0299maTU9d1\nGRoawnEcyuUyo6OjHDp06JmNr9xrNuto3qmZrKOjY8OuMr5Ou2UGrVhzGXamGTu9MAwZHh7GdV3e\nfvttxsfHm77+ZvE6N1/GshRaabI5C2kJbAHKVmhC7CxEUyMj6UDGgdAKIqNyCbmsxvMjK51qVaOF\nRSarcH0LITVuWUS+kGHUZe3kQnCjj8igVlfkWQEaB2EpRBgvxgGWAGmFaOFh2yFaKxwZedZaMkqG\nhRU1glh2ZD30+lvbT6LbinZthjfraH6aZrJ2kG4mMZvP9tFsMjszM8P9+/d58803uXPnzlP9zB/8\n52TiGoWCqCedkM2un6JoNH5tPRW1tUyqrL6vyNQTy2xu3ZIr1wm6XNfOOhqdch7Q7ro9V+iLBueC\n6GdmCjpJcp20vZfc6FwggvW1TKkQYRGdvkRzFZBSo4OoIitkXWIkNUpqnKxFEFQQgYeVkZF+QoBl\nS46c2H3cN66LQgiKxSLFYnHLZrKenp5EcxvH+4t4+nkgkllYX7y2Suqmp6d58OABb731Ft3d3YyP\nj7dsNdKMCfTc3ByXL19mZmamqes2k8zG1eRarcY777xDoVDg5s2b9PT0JIuE4ziJTmY7+6uDTDMd\nzc00k1mW1dahCbEH3najhg2tsdPiGHsxnzp1isuXL+O67lOZtv/5Xy6yuhp1XTmWpLLmUSgKyrWo\n2cIWAD5d3RbV1RCrCOX64hfWdDSStgCqAkhFUI6rq5HXZaYDwmpUOXXL0XGmgyJAki9IAjf6OZ6n\nQUpyOUXgWmQygiAIECIEV6C0wpIWoa+iCUFohNQ42HiewsmCW/MQwuPtdy61+mdPeFbDSZptJlNK\ntaVifFCqPC8aO8VrGIbcvXsX3/fp7+/HcZynnli5sORRKOjI51VHI6CVBi1CLCtqhs5kFaom0WiE\npQlTvrEZvZ4L6CDlFWtp/HrKmitqqqXo60whRHhxk1Vj9XX9y7RzgZVZP31xCusSA+lorHi4AhpH\nyWQkrdKRJlY40QjbeAKY8jTCBq1r0eheHXnNxj9MCOg5vPt7upn3otlmslaaAXci3oDud1/KgUlm\n42BrTGbDMGRoaIggCJIgg62te7Yi1odtRtqftr+/n0ql0nSlYaeKr+u6fP755xw5coTe3t7k9xNC\ncPjw4WSRqNVqLC4u8sUXX7C2tkZHR0eS3O62KeNZ0+oH31bNZA8fPmR1dZVSqbRhV7mb6nUYhmSz\n2X0PtBeN7eJvamqK8fFxrly5QldXV/L4VhvG0o//P//veaCueXWjf491s8UOiVuO/s2tRK9J131w\nZKqpK5Ym2DmdaGmDauRUYElQCLI5UHUtbehGQxOkUMgg0tJKDwQKbSksH3AUoLFtncgIwhC01GSy\nEtdTWFIS+BphKXTogtZ09mbJZHb38dtOC7xWr7NZM9nCwgKe53Hjxo0NR5y7+dwysqD2s5PMYG1t\njdu3b9PX10dfX19yT7TqJZ3mR58u83h2KfnvnBNN0wMIXIhnDGSzFmEQvS4pIVBgWYJcQYKvwBIE\nKNwaka2VEJTLGl0fPIALfj2MchmJp6NktaMo8eqNocWCReBGzgUZW+L5CmFLpBC4bohl2QgEfujj\niAxCC8gE4EZH9DKvkNR1tX7kJyulwhIC4URJuGWHqMBDBx5Ca7SUOBkLaUuE0GgdeVG/emlz95Zm\n2E01datmspmZGQYHB8lms4nN5tM2k3me9/+z964xcp3nnefvvZxLVfWFN1Gk2KIulmzrfrFI27ks\nHAww3hUC2VisYXk/BI7hT3EQI8Bu4ACJkDUCBFg4xsIxgsEkWOQy63gSD7yenZn1xjtZZzJ2ZFm2\nJbFJUaJESrxJJMUmm91VdS7vZT+855yqbnY3q5rdUkvm84WHXadOnTp1nvM+l//z/7+jscqWCGZX\nc7aFhQVmZ2evcjJYedp5LVutMjs/P8/s7OwS9aFxhrrWqsxevnyZw4cPNxREL7zwQrPv8qw3TVNu\nueWWpmK5uLjI3NwcR44caVoF18MQ8HbZ9Syy9TBZv99n27Zt7N27t8kqr3eY7MbiuHEmhEBrfZUI\ngjGmaU0ePHhwSQv7euU0n58NU9FShUqqGC6vVGDXKKmkaAEqCEDakpQLHlTAywpEs6i2JgSmC+Ap\neuG1wGcp0Wk1pQxhIUQSt8D1w+QyRc2DGj5eCYXBITVN5caUHuEFSgmMy5De4HxYhO+8ezSs/2ba\n9XLD1sNkSZJw/vx5Hn300abFud5hsq0yTPJes5Uw7t57zpw5w8mTJ3nggQeYnJy86j3jcEkP299+\ne9DZFHIQyGo9EB7wQNEbPBM6LUm+6HDGkxc5utSAJ51wUFVMdcsj8+rdwmHyACsCKDHNwFYpLaJi\nLvBxgajxshNARauH9lWVVuDxRM7jXR7S4MxiTRA5cKXBKoFUnkhJPA7pILYCn5eBjSEWmIpLOkk0\n3npMadHC44UD4VGuYN8dqzNCXMs2Iomth8neeOMN7rnnHpxz1z1MtlUoR7dEMAtLFzvvPadPn+b0\n6dPcf//9VzlZvf84pfLlfLDee06ePMnZs2evkr0dlzt2+b7ee06dOsXZs2evUjYa9ZjDQxnWWi5f\nvszc3BzHjx9vcGt5nr8nh8nqAHSUYbI6q1wNmjGsTnJjcdw4Wx6cLiwscOjQIW677Tb27dt31f7X\nU5l941xBtxs+K1ae3AjSFs2gVz0AIpXHlWJZUBv8oz0xNARWyV5W8xjEbYGvjmXyMFgSp4KyDFUX\nX4a/WRvgB0k70HuBx5VBASiUbTxRVEEMBEGTnQJna5ofWclZwoc+vGfka7HcNnIAbCN5oYUQTE1N\nMTU1te5hshpmMC4v9A1b25RSS9YKYwyHDx9GSnlV4lnbuD47bD/52QAvGw1TcqUSm4VjpimD9r/0\nTXcFQLohcQMGeWucDjosquWwlWqXTlgyIDaMnTW5Q1JjdC01j4Jue6iws7oNMh9gZ5WRgTnBBwls\nIT2RBGvLADGQ4XmA9CghcEV4DtSvucq3QvKcI4RhcufUuq5lbRspclInjXEcX/cwWX1u77RtuWDW\nGMPs7Cxaaw4ePLhqFXI9i2O9+NafEUXRip+xXk5aCDdJ/ZA4cODAkmMP7zvO4JhSaklQl+c5c3Nz\nnDt3jkuXLnHu3LmmlbCaaMS7yVaT2lvvMNmNtuXGW+2vdeJ25swZHnzwwVUVYMZ9CA93Uv7tt98k\nisBZh7Wiatt5QKIjH6aGAemDnnucQrEYBA/qiqt3oboatR1lT+HxFL36GJYyV0SJbyabfekIPLMO\n21WBR7IcMBs4IGoHWVqpADyxVOCDZrzShrIoEaKiEBPgnUNKRZQI5ubeBJYOUY1qWzWYXW4rDZNd\nunRpzWGyG8nn5tjw7zw/P8/hw4e5/fbb15wjWA83rfee+QXD/PygChypASWXVgMaLR05TIVr9cKC\nD898ndBQank8bkiO1mQDCVqGvlPSBrNQbXcEvqq+Sk3DBQ3g8yFp23JAz+WHBsR0yyMqFgUiixQC\n6YDSo2XgrNWRwPoglCCjoACmhEAKgS08Qgu8KLGuR5gWg9vef30zG5shGT1s6x0mW36Md8q2VDBb\nO9kdd9zB3r17r7n/etqWdQVpLUceB2YwXMXt9Xo8//zzzMzMcOutt6543OFgdr2WJAl79+6l3+8z\nOTlJq9VqWBjKslxRCu/dZKNig0YZJut2uxhjxmpbfve73+WLX/wi1lo+//nP86Uvfemqff72b/+W\nP/iDP+DFF188DDzvvf8fx/2e71Yblsd87rnniON4zcRzPVYnn8YY/sN/Ok7ed8SxaHBwTgaqq7il\nKU2YNLYm0OVkmUMqSdSWFD2LEJBl4IUg1Zq+zGh3JGXmQAvyApwSdNqa/qIliSV5hdez1mOVJW0F\nDK1Skn7PIYUE6ejlhk5HkvVzpHYIa5ElxJHEVHRdTniEAoHEu5z9t28nTVNOnz7NwsJCIw/9diej\nG7U4juqvWmtuuummRm58pWGyOI4bXx41+bzhr6OZ957XXnuNN998k4ceeuiaz8NxC0b1uvmv/vyl\n5m9a1/R5wQKmvWIBGUIJpqnCV41WoYPcNITAVNR/Vx5fCZd4arx7MFMMglwhXY1wJe6AX6wHvAbV\nVyFZMiAmhs7FWYeqA2btscrjhUdGFSet8GA9ilCJlYATBBaDWOGtw7kMrRzeg6jwvXd84PqC2Y2U\njB7lWKMOkxljqiHY0Z8lo/rspz/96SOEwvw1fXbLRDoLCwu89dZbPProoyMFHeupzNYDVmtVkMY9\ndh2g1hKd991336qycyvpXV+vLR+iWn7DSSmbhXI9VaB3wtYDdF9tmOzo0aP8yZ/8CU8//TRf+9rX\n+LVf+zV+6Zd+adXjWGv5whe+wPe+9z1mZmY4cOAATzzxBPfee2+zz7Fjx/ijP/ojfvCDH7B9+/b7\nhBC71/1l36XW6/U4d+4c9913H3v2rL9lvprVAwVPP/00b12sMLFKUBhPnAhsNd3srcNZTxS5oLQl\nQZgwZSwSjzSeuA0+C4o93fk8yNW2HaLwSD2AEeTdAgqBSgWu58OAmKlwtjrgZGWbIL0JFH2HFNUE\ntrfEWjX7WxNakDqWFMYSx4qyzJDScuAjt7Nnz3QzRFUnYUePHqUoiiX4+JWS0Y3mmb1eWy/Nz0rD\nZK+//jqvvPIKv/d7v8euXbu46667+MxnPrMqbvOGv45mZVnS7/fp9/scPHhwpN9r3IKREILZ2Vl+\n/NMeWgecrHMlUoTfrtWmYQ5AOGw5qJIKIxoogTUDEYMk9hTVQGbSsbiurrYFRbfmjaXBsNfHar4D\nUNeI42QATUomwPWqTksCqqbkEkPbBHyvpJKkrrDwXlb0XNQQAz8EMTCUphsgTE6CFAgBcUtft69t\nJMwA1tctWz5MNjc3hzGGT37yk5w/f56vfOUrPPnkk8zMzKx6nHF8FvhF7/2lUXx2SwSzZ86cwTnH\n+973vpGrZ+M4mrWWN954A2vtqvigYRsHMwshED9x4sQ1lVKGb57NUrlZfsMVRcHc3BynT59mcXGR\nVqvVvL5VMWkbwYFXD5O1Wi1+93d/l2eeeYZf+ZVf4fz582u+75lnnuGuu+7izjvvBODJJ5/kO9/5\nzhJH+7M/+zO+8IUvNO1T7/3aB32PWZ7nnDx5kunp6U0JZCEILSwuLnJp8S6cO7HktSgKAaJSQe4S\nBFKG9mXaFphe8KuyF6ACUSQpMoeMB4tpkL6VtDqSYiEEwWFAjEAlRFgwa2yszUMFRlYQAxkHGAOK\nUBnG431Q/4pSSVlapAhcmUI4bJmBD/CZPbdMN99llGS0bsVPTU01z5B3A8xgHKuHyaanp3nkkUf4\nrd/6Lc6dO8fFixfXrPjf8NfR7MiRI0RRxAc/+MGRf/NxGIMWFxdZWFhgz549HD/xRsCTApNtSZF3\n8UBZiFDNFII4DbK1xguUFjgROilKgRKerDA47+nlAqtDghj5mrlAkEQaEwXfn57UTcU3jj2FkQgV\nWv5FP1DpSQR5zyCJAnOByVGEyXuhSzAh4NYti6yU/oT2KCeQViLxCDWg5xKqYkRQBGoGYXG2R5E5\nVKxQWqEq7lkhYXr39a+1Gwkz2AiLooidO3fS6XT4q7/6K5588kl27drFlStX1nzfOD77+c9//hKM\n5rNbIpjdt2/f2MpYozpazXc5MTFBp9MZqe0+KsygLEsOHz6M954PfehDG9YC2EiL43hJ9aMGeNet\n+OnpacqyxBizZSAJmyG1l+c5n/jEJ67Zujxz5swSiMjMzAw/+tGPluzz8ssvA/CLv/iL/PCHP3wa\n+APv/Xc35ITfBZYkCQ8//PB1k6qvZMNUfJ1Oh29+J6gIaT2g5HKV3ycplP1Q+bBVkGpdFcC2aKo1\nIXAVxKkkKxwoD9WwiK3ovVoTgrJiNqjpuup1I2qJRpUoDJNIdBLYChKtMMYidGhBUrEbBLovgXUZ\nUhjQgTt5evvaqlYrJaOXLl3i7NmzvPTSS7RaLcqyXLX7M4693TCDcY6VZRn33HMPv/7rv77m/jf8\ndTR76KGH+Od//uc1udyX27W42WurOeCnp6d55YRuAllRJXNQDXFZj63W+DSRldoWTEwq8oUC2w8V\n01qgJIpBdEOQ4oXHGlA+3K89mzdY2DLKGnUvEdlGdlq1Hb4e6oqBrOI9EUDm8PTDsFZuUUgEEmss\nWgSRgyiBktC9SJM4nK8TeCvwFnAGkXryfAEpPXGs8Q6cCUph3lXJosmY2nXzdfvJRsIMNspqjHu3\n22Xnzp189rOfveZ7xvFZIcQPCAX2a/rslohepJRjtzRGcbQ333yTV199lfvvv59+v0+32x3p2KM8\n4Gvs7f79+3njjTdGusnWOwC2UbYSwHt+fp4LFy7w3HPPIYRYAkl4pxxnM9RJ8jwfiQNvpd9k+f1g\njOHYsWN8//vfJ47jzwD/JIS433t/+ao3vwetpuYadzikThJX+21rzPm+ffuYmZnh6aefZvbFkOW3\nUknWCwTlpsK3yYqfSyVUg1u+aVe2WoJ+4Yma18AUBpC02g7TU6GaWwWpSglKPK1JielWEIZqajqK\nRZCujQaDKa6sMHOCoNseV5ycIkhiOlmiIocrDTrS4Xsrya13jjfUNDz0WCejL774IqdOneK1115r\n8PHbt28fOxl9p2EGK1lNBTXqANgNfx3Nhukvxwlm1/Jx51wDjTl48CCzs7P8u785M/SZQSsBIIoE\nYphNoRis3bYcfEZpDFEVliQtcJXwSdIWUGNnI5aoftl+YBmBgMMdqH4Nyeem4KvlP24DNXZWQFRW\nzAV4IkEY9sLjlUGqILpb5D2QPoigoPAYpPH4RYEUARcrlcRgkEIGrloMnpw4jelnPZ599tnr6oxu\nNMxgI6z2/X6/PzLGfRyfBT4GzDCCz26JYBZoAo5x9l/N0WrFrRofFEUReZ6vm2ZkudWZ6IMPPkia\nppw5c+bab+KdD2aXW93CTJKExx57rCFUPnv27BJ1n9rx3i5H2sjFEQaOMsoxZ2ZmOHXqVPP/06dP\nXzUoODMzw0c+8hGiKMJ7f0II8RJwN/DjDTvpLW7rmXSusegr/Q7nzp3jlVde4f7772d6OrThz10w\nWNMnicNv2GoLtA4LpHOewoBKBDoSCO1JYgXO4mLIC4eVnk5L0beBKqsoAkescw6PIOlQKYF5+guG\nIH4Z4AdRGmi+APJeiUSjE4vvB+lbaw1SO6wThPBZIoVDtzwuz9DC4axCSQCHkpCkkr371s+oMZyM\nzszM0Ol0GkjCyZMnAZZAEq51v28lmEFt44om3PDX0exawgkr2VqzI7W0/N69e7nnnnsQQiCl5PlD\n880+SSxxVWW21VZk3YBejZLAFQ1hqMtkg3swGlL9Cu+tFLmUx1UBa9wW5PMVFGhCNFXZq5gLimHm\nAjug55KOWuhWt0HU2Nl0aChMgrIS5QkDX04E0ZRY4p0JYFod1nBZPzlKg1ACLxxClThbIKRg9627\n2bVrF3feeWfTGT127BhZljE9Pd0wflyLz3crBrPDldlRIaLj+Oy3v/3tEhjJZ7dMMFtLjo5qqzla\nv9/nhRdeYPfu3UvwQaO2TNYy5xwvv/xyEyTX1anr4aTdSlYTKu/evbsZyBjmnJuammqqQOsh0h7V\nNjqYHeeaHzhwgGPHjnHixAn27dvHN7/5Tb7xjW8s2eeTn/wkf/M3f8NnP/tZhBC7gPcDxzfshN8F\ntpy3ctT31PLCtdU+1ev1msSztu99v0+/lyFEqMZ6F0QQbO6IkkouiKDnXuQOMQG2D0KBrjr+ZT9M\nKkeTgmIhDHWVizVdl8BaR9oOx0QIsm7grE0TRV464lhircNJoACEoDOpybMiDHUVBiEd1oBzBlVq\nrLEoITCFRWiBkhJrMlqdjaWGW059Nazu8/LLL18zGd2KwexwpWeUxfGGv45u6+l+rrT/+fPnOXbs\n2FXDzvMLjsXFwf5K1HIm0O1mqCrcSFNBsVixkgxRcsVtGmiQl34JjZbJ/EAcoQhQIgjwo3rAK50Q\n2KqSG7VB1NVXBaIYDnIHLAhuKMhFWmp6LtnyeBxOOKI4MJMIEWJY7yr8fNACxCuQkazIcD3CZ9jS\nNywG+98/Q5ZnK3ZGr1y5wtzcXBPcrZWMbjXMLCxNPketNG+Wz26ZYHYjHO3ChQu8/PLL3Hvvvc0D\nvrbrIYCGMPTywgsvsHPnTj7wgQ8sGcRYT4C61QPbeiCj3W4zMzOzquPVBOgb6WQbHczWNso5aq35\n+te/zsc//nGstXzuc5/jvvvu46mnnuKxxx7jiSee4OMf/zh///d/XwPW/z/gf/beX9zwE36P2fKE\nMssynn/+eW666aYlPlXboSOhUxNFEm9rLcxKorKj6M47ohjKGktbrWqtCUm54PDCV/g70bCupx2J\n7fmGZ1YgUJHAZWIJJKHsGqQRyNQickCCrEQTyn6OdBZhHQhLFImAp5PhXy8hTjVFYVFaUBZdhHR8\n4N6rxSTWY6sFoeMmo1sRMztuZfaGv45u66nMDqtsOuc4duwYi4uLKw47/z//eXHos3wVdIZWvhKq\n8UGTD9a9dqKxFXzAYVFVMNmZEtiKN1anblAxFeCGGrh+iFJLyQGHbZIKinyAh2+C3NZQ9VaAMoP7\nVgxdGucsSgVGEuNtYDCQHqIw1OUFeBP8UCqBjiRF2UeYPETxWiGkIEkTkjQhL67uOtdDynVCcK1k\ndKNgQRsZdwwrbI5amR3HZ4UQRwg/6zV9dksEs+tpgQw/hL33HDt2jCtXrqzKKDDOZOZyWy5Lu/w8\n1lOZ3erB7HJbyfEuX77cZOlxHFOWZcMPeT2L5GYFs6Pa448/zuOPP77kb1/+8pebbSEEX/3qV/nq\nV78K8MDbe3bvXhv2wZrK7p577mmGnYbNGMelSxUlVyQobGhVmiy8P+uFVUxU8rZSDRY2bwdsBL4f\nOCnzbhW4KoHFE7dEqLQCZTUgFlgIgiStL6tZLhNaknELfNXO9DbIVnoBomozegJ5unVVUGs9Xli8\ny6AKPu97aD9nz57enIu7zFZKRhcWFrh48WKTjBZFwcLCAkmSXJe/bVYwO+rieMNfR7PrKRhlWdYU\ncx599NEVn+/P/OQCtVM5C0YIIi1ptVRApHow1pOVDq0VWoHSGj1RJ2iGQjniSIGQlDp8dpoqCu/B\nQ2dC0VsMvM1JIimLECwLIckXBvCBfrdAEbo8ZZkjCTGB0KamRCBqDyRvZeSHKLk82gVkrCQEuUKB\nVAKBQ8ciUHGFXZGRoMzniSKJiKOm6qu0ZO/te0bugFwrGdVak6YpZVleV2d0M/x1HMwsjOWz9y5/\n72q2JYJZWFk7ehSrK6bbt2/nQx/60Ko3zXoqs6PI0o4TtL3bAti1LIqiJQToCwsLzM7Ocvz48UbM\noa4CrUVXtpJtlLPV17osyy3D1PBesfUkK7WPHzt2jMuXL/PYY4+tOpT3nf94ktpV6sERUTUthfLY\nqoIaSUmBI0rBZYEGx/RDGzKNFf2+Je2oRiozvBbwq3nh0DFQ8VKWeQhq006o7CIZKIK5AD+I2gLr\nPSqqYAQSUMGvtVKUNgS3psxAlKg4wljH9PYJtN44wvNxr/8wATqEAYuf/vSnvPXWW7z22muNNPSO\nHTvGTkY3A2bQ6/XW5AK/YePZejGz1louXrzI0aNH+eAHP9ioUK5kZ9/ImoGvyY6iyCymsOTSNB2V\niamIslviTYlsCbLLoSwrdYAGKcBkBXkO0lV+KT0ir7ZjAbnHA6otMV2HB9Ipies5PB6dCHwBRniU\nVJjcIuihlKQobCW9HJJeqSpxhViQlyEwTtsRxgTdaRkJnLMID8pJnPVI63GRwFHiKYiw4KH0AhXJ\nwDcrJK7ocvd9d1K48eOalZLR48ePN+xMtTT0jh07mJ6eHsv/NsNfx8HMbpZtmRV+PQMlxhieffbZ\nFSumy23cYNZ7z6FDh1aUpV2vCSGaakj9Ge8VS9OUNE154IEHmirQ3NxcwyE8juNtlLMNL4w3pGw3\nx8YJrLz3vPjii+zatYvHHntszff9w385SxwH3JmxFilpGAFabUnR92Goq9J5VzLg81RsoNB4PL2F\nMlRqVIWtTZYOdYEiaUGxEALkulLrTB3Ugu2FKedaQ15HoL1Cao9MVOCZ9KGa5J0naUmkLoJ0rYqQ\nShBHmg/ctzqJ+Li2EVhXrTVaa+6+++6GPWBubm5dyehq8tPrsXFhBjdsdFtPMDs3N8eVK1fWTDwB\n/v4fTjWBrBB1YliZG6xzWvralbDeNLACnQKVBG3cFoiiPmmPr/ifAzRoUH01eWAnASrVr4qPNhG4\nCvsetfyAtguHslDLMyjnKuYCQFh0Gc6lFHmASUiH8FHgqZUSInDWgIRIg+svIpVHp0mg5xOiYjGw\neN8njiOmd27n/Pnz1+2vUkrSNKXdbnPLLbc00tB1Z3ScZPSd7qRslm2JYLbOGkcNNr33nDhxXxCl\naQAAIABJREFUgjzP+eVf/uWRJCDHCWZ7vR69Xo/9+/evKEu7Xuv3+5w+fZqpqSnm5ubw3rN79252\n7NgxdvVyq9nwAjtcBbrjjjsax7tw4QKvvPIKcRw3wW2n07nK8TYayzeONOYNG91qnxol0at//9tu\nu433ve9919z/h//8GllmSBOJKR1KhWEuABdrbGmIOhpXGISAvBfaflGk6Bc9Wi1dUXEJyCRGejrt\nmN5iQRRJjAtlIGMVRjumpmKyxZIoUhSlBS2xXmAUpInAFiCUoN8PgXWqJVnfkqQKawwy8jifQ1mS\ntgInZZxEmNKBLHjsw3fhWT9mfzNsGIPXarXYt28f+/btw3vf4OPrZHRYlWylwZTNWBxvVGY31sYJ\nZoui4NixYzjnRuJQ/9a3X222haDpqsTpYFBzkGBWw1vIRvWrLCxRFdjGqWyou1oTCrtYwY1aAlMH\npoqlA2L9wP8MYEozYC7ANsFv1AYq5gI9LG2rQA2xICgnguqXkIi8QEiHiKDIAOXwLoBmBSF4Dj4e\nuHB14imLPkIJdu3bG85hAxX76uMsl4ZeKRmt19jlscVm+etyRoK327ZEMAujwwyKouDQoUN0Oh3a\n7fbIWuajBrM1li9N0zUl2cYx7z2vv/46c3Nz3HHHHdx8880cOXKE3bt30+v1mJ2dHbt6uVG2UdXh\ntRxkueNlWdYoHHW73aYKNOx4G/H9h/E873TW+F60UXgraz348+fPs3fvXiYnJ6953FdevUSWVTQ+\nWmJLR6sdhWqqgKJ6TUqLdaGq44vQesy7Bmkh0h7XAxF5bM9WcrUZooAo8Zi+BwmFMdVQl4Xco2Ib\nmA2opGy9gFhA6RExeG/AQ9YPkAakxpDTQuKsRSqBKQxChnajs3067YTORJvFxcXVvvJYthmL47AJ\nIa5KRi9fvsxbb73Fq6++ShRFjb92Op1NaVuOygt9w0azcWAGly5d4siRI8zMzDA/Pz/Sb/vcobea\n7VYaWvsASgWmDwjqfLX4CMoNuGLFUkouU6n6AYOoGJCxgH411DUpsQt1kDtEqSX8qswF1gwGzLwY\n0H7pFoia3isVCOFBeVQcElUEaCHwpkR4kBUlF0ogtAyYeASeLLCqiKA+9sGH77/mdRvH1vL75clo\n3RmtY4s6GZ2ent5wf9Vab4k1dssEs1rrawab8/PzzM7Octddd3HzzTfzwx/+cOQH+1rBrDGOb3x7\nlsNHTyGFYWbfzVw4/xZvXJwlbUWksabTjmilmnY7opVEtFqaiU5Mu732A9day+HDh5FSMjMzU3Md\nIqWk3W6ze/dubr/99qvaBmmasnPnzk2Xnd2oYHacBTZNU2655RZuueWWFR2vDnanp6evC94xzIG3\nVaV7361WCycYY1btKpRlyaFDh2i1Whw4cIDXXnttpMX0L75xtPoMyKvA1Ve+22pJygpaYItw70o5\nkJitx5nLalAsbSv68xahwOZh0XHVgFh7QlF2LUH1yy9R/dKJQFT9UJNZJBIZgbeBXaG0JUJC0Tco\n4TEmBMdxojHWgnCYchHw3HHXxkEMYPOD2eWmtWbXrl0NlGt5MgrB16ampq47AB2uFm81taN3u12L\ny70uupw7d45HH30U7z1zc3PXPO4bb3bpLg7RCtjBOlvkgwAyjmSj4iekb3i7krbAZ1XrP6ah50IE\nQYQ6HHXFYK1S0jfMBWlbUsyHg8UdD/1w36jEI2u2AsnQgFfFwlV/bzcIbJ0MHLJCerxweBkYDUQi\nsSVBIMEDLgyd6jjClAXe9VGi+iAhkEqx+5Y9zXV9O1lDhBBMTU0xNTXVxBbDyWg9u7O4uLhiZ3Qc\n20qwoC0TzK41HOW95+TJk5w9e5ZHHnmkuWgr8VauZisFsy+/epF//dc/4/kjF+j3c3qZIdGKyc4V\nFhYzfvD0JZSCft8wPRlTFA5rDdumExau5CgtmWxH9LoFE//qGJEOAPPp6RTvLN44osghpSKNFftv\nm+K//x8eXvHmGa5e1pOMFy9ebGRnt23bxs6dO9m2bduG4HeHbaMcbT3HWcnxfvzjH3Px4kVeffVV\ntNZNFWhiYmJdgylbIWt8L9palZ468Xzf+97Hnj3hoT5qd+S//jCIkGg1UBAylUqQ1oIS0PGAiqte\n1dKWpFx0CAWuZiqo2pXtCUWxaEGAzWrFoJp4XeF6Yb+yH8QTdBIqwjIC6WrVLxu4JnUg0IykxDoT\nqHiqNSaQfhkUFufCgMnBX9zYCs1G2np8dnkyeuTIEcqy5MiRIxhjGsq+9T6r3kuzBFvJ1vLXsiyZ\nnZ0lSRIOHDjQSAqP4q9//W8PIUQfYwIVHVKTtmW4t7yjrCT78lwQtzVKyqC4V5QksUZqE/DuUhKn\nnrxrEQhaHYXrB8JoqaDsD5S68isWUQXJ/W7eMBc4ZxrmAqkNmPD3pO2hEmeQkR8SV/ADfC4grEXh\nUULgi5CoSgWuLNFKIBVhaMxVya80KJUjlWqqyDpS7L51T3PMtzv5XG7Lk9ELFy40CoJ1Z7TuCo+b\njN7AzI5hxhhmZ2eJooiDBw8ueTiOg4Ot93XO8X/83WH+4/de4Y1zXTyGsvRICZ04TGCSKiIh6PdK\ndmxLyV1Jb9EwOaFZvOLIuoZOoulnJcJptIDeYsG26RSTGebLLp22ptctSFJJHBkWL+fc/8Cukai5\nhicZb731Vqy1zM/Pc/HiRY4fP47Wmp07d1IUxXU/+DfS0TaikjI8mAKBraJWOFpcXGRiYqIJbq/l\neMOV2Xc6a3wv2kqLY80AcubMGR5++OElD7hR2pxFYbhwIYBj4zjg1NKWxFZBaV5VXKVyeCuJ4iBh\nCUGBByStCUFZCSSYqrIjRHh/Z1JSVBg8W1WJkljQ7wXJS6pFTlTT11EKrhemraNYonSg5tJKohOF\nsB6tBMbYQMnlCrzNEDoCBEorTp4+zkL3Eu12e0MCta2kBFRX6Pfs2cP09DTWWi5fvtzg964nGd0q\n3/G9Yqv535UrV5idneXOO+9sEs+19l9u3/t/j2PKUPGVCPJeSQ50OhpTdVaEBtcP8BypAnLHe4Lf\nxR5hBaZ6v6j4YWVb4YoKrpAKyszj8KQTGptZEB4dS2zpcQIirbCFwcp+YCwwFqFkJXYgggwugiSR\nFGXVnWlH1SCZIE002JCOCgXOO6QIBRFvXeCXdQJjC1AglcUtFAgtkT74ukBQFle4/e5fuI5famXb\nKL/XWjM5Ocndd9/dVGjn5uaaZHQYH3+tZLSemRiXmmszbEsHswsLCxw6dIjbb799RXDxOID2107N\n86//+iS//7+eoN8vK133itYjkiws5GyfTjHekvUMrSTI7+V9Q7sVkXVLvFUkUUTZL2lPJygL/SsF\naUtRlI6ib+i0IrJehikcibbYokAmCVJJPvyR29a1mCmlmgUBBm2+etF466232Llz56Yrc61lm7XA\nJknC3r172bt379iOt5Wyxvea1fKVw/5njOHw4cMopa5KPCHcx0VRLD/UEvvWt19E0COKAhwgikOF\nR+rAXoCAuKVIE4VMQUiLKR3OOgrjQpVURBhlmJiI6C4apBD0+1SyPQonPWlLYHMAQe9KgUChY48p\nBSoKikMAZW5QaFTiKcsCKTSlyxHKU+RhRZYovLdEkQ2clkoG3B2eO+66mUcffZRLly5x7tw5Ll68\nyPPPP99AiNa7AGylQG+4/amUYufOnQ2F0/Ukozds42wlzKz3ntOnT3P69Gkeeuihq56RowxlO+c4\nffpK8/9EC8oKDlAYWzcsmJqK6V8Jvm8x+Er1K0oHcB4vPLZXd02g7Jlm25hAtycQKO1DEdQLkpbE\nLlQBb1JXXz1SWWTpB3R+ziFc0BCzzqJNzVyQERVh28gMIUqc1MSqhbdBLMFLsDikrMQfAGe76DjG\n+upZKCTeWxw5Wgn23/3+Jdf5nex+rnSc2l+FEExOTjI5Ocltt902djJqrb1BzbXcll+kM2fO8Prr\nr/Pggw+uOtU6ihDCN791hG/9+5c4c2aBsjQkiUYKgbeh5dhONAJPO9FI79gxHYfpZDxTUxqBIIk1\niQLlod1WWCuIpGTHthTnPDhDeyrBW4vwBWnsUAKiRFMagbOOnTsnaLWTDRFNqNt8WZYxOTlJHMdL\nyNB37NjBzp07mZycvObN/063QMax1Rzv0qVLjePV7ZLJyckb1FybbLWcM8Di4iIvvPACt912G/v2\nrax0NYq/fvv/eomiKIniQTU2rVr+rY7EZA4iQbcXfCdug88rKctK2SszORIRtNILS9yRuIpfNpsP\nOuuyHY6pkyCQ4PDkXcAKkglN0TVEscKVFi9FgDJ4gYo1JodIaZw1COXwwmFdD0HSVCqtNXiR89Bj\nH0Qpxa5du0jTFCEEd9xxBxcvXmzI0KenpxsarFEgU1utDb8Wlm+lZPTSpUu8+OKLlGXJtm3bGlhC\nfYwbvNCbYzXGHULieeTIEYQQKyaeEJ631wpm/8N3XxmsaXIA6wFQ3g+xFQwwta1U4yvVL698E8ym\nEwoqP5UJCFOvJz5wSFf/K/qmYS4I3ZgKPqAHfCHxhMA30raiGQoTaqnqlzJDa5YLTCXKGAo3j4h0\ngCxYhxTgcVhp8GUvBPomSGB74dGxosgXkEoyvXNQ3Yat1/1cy19HSUbrNTZN0y01ZL3lnhjGGI4e\nPYq1loMHD675ULtW5vhPPzzJ7/0v/8DUZEzWL1BSoEVM3i9ptSQ6UvSvlESJwBaGha5jerrFwpUM\nJT2Tkym9xZwy1cSxotvLmZ5qYUxJd94wPd0i62WAY6ITkWU5OpKkqSbLcqSKEcKRZTkH77ltyQ29\nUQIKw5PHEBaCmlLnypUrdDqdpgq0UiXk7WAzGMfGOZ+VHO/SpUucPn2ahYUFlFIIIXjzzTdHmqIH\n+O53v8sXv/hFrLV8/vOf50tf+tKK+33rW9/iU5/6FMBj3vtnRz7p95DVlZ6zZ8/y2muv8cADD6x5\nnUfppLz40oWwrxRYC1oJbOmavxmq4ZHAaY6vCr1SBGWvdELh+q5S/QoLXRRJ8r4jSkXDF1urfulY\nYI0PWD/jEQT+SukgisAWgPf4MgSuRb/A4onSNr1+TiwcZVYipaAsDEiB0oIi7xMlmrs/eOdV37HV\najEzM9OQoc/PzzM3N8frr7+OlLLx19Xa8lsJZgCjV4yGk9H9+/c38Kl6mExKSVEU/OQnPxlrYHNM\nn/XAgZ9Hn63Xy8XFRQ4dOsStt966JmPPKL/pv/vO0cH+Q3+Pk6DMB6HiWmaDiquwgjrMdaVtaLS0\nDv4NldrflfCsiCYUZQUNkhGDoS5Bw0ELg0FNAFsOtqUecEjrlkBUFH8qFQMWBAkyIJPQscJYAzbH\ny5zSCaQEWZZheFRLvAyDY0JLPCVl3g/XS8D77rvvmtdtPfZ2D5LB1clot9tlbm6Oo0ePUpYlZVny\n0ksvjRXMbpa/brlg9plnnmFmZoZbb731mj/ctRbHb/ztLBCGR9KWJu8brCmQErKuYWJaUOQlJoeJ\naU224OguZKSxwGQeWxQo77G9Ehl5pHH0LnVpT0VY48kXeoGz0jrKoiSOw7aUoQXbz/pMdDTGOP7b\n/+4xnO81wfdmLUZRFHHzzTdz8803NzffxYsXlwxnLOeL3EqV2es5TpIk7Nmzhz179jRcxK+88gp/\n93d/x+XLl3n99df54z/+41WPb63lC1/4At/73veYmZnhwIEDPPHEE7Wme2MLCwt87Wtf48Mf/jA/\n+tGP1nWu7wUTQnDy5EmSJLlm4gnXxrgfOnyOoqL0ibTAlpXGet9XXLLhtbjigk07ClP9reiFoZFI\nS3IcOpXYCiqQ98ISmbQUeWmQEY3ql62wsWlHUy6YKkAOC6811ed1wuKmpcJW1di8t4CwQedSiFCx\nDfeuw5YlQgh27919zWsopWwqk3B1JWQl2rqtGMyuZ9BrOXyq2+1y6NAh/vRP/5TDhw/z6U9/mj/8\nwz9s8PMr2bg+C/xcOmwNC6oVpK6VeI5qLxw632xPtEPSCIFhpC5LtDsDXxTK4WpcugTtBvfNMKzA\nmcFzQg7dWu0pTXE5+HN7WmMXKwq/VAyGukSovtafr1xIdCEkxK7a1hXlHoBuh0DUC4/QVPQoHqEE\nzhV45xFC4l3A9UZpjCsN3veR3uKRCCGRQhBPbVsi675RFdV3IpgdNiEEExMTTExMNMnoT37yE37w\ngx/w4osv8qlPfYrf/M3frIs8K9pm+uuW4T5588036Xa7vP/972f//v3XTbcF8Nzzb5IkiqKwKBkW\nxCLzpK2w6Jrc0pqoFmAPKhK40pMkCoSn6DvaEwGDmvcNSVtBRe0jK014pQVSgHWOOI7Ae7IsI04c\nwhuMtXQmUvbN7LyqGrvZ7cL65rvtttt45JFHePTRR9m2bRtvvfUWzz77LM8//3xDin699k472nIT\nQhBFEQ888ACf+MQn+PKXv8xnPvOZNc/xmWee4a677uLOO+8kjmOefPJJvvOd71y13+///u/zO7/z\nOyNzHL8Xrd/vc/LkSeI45qGHHhqpLXyt5POvvnGo2m9AyVWWNcXWYKGq1YXq+7Y1oSrlHbDVa63K\nxwNpe3irrwLXJK3akoqmYlOrFKWTAVqECEFteMkivEdHgZdHS4k1Di+DkIKXEMcaJwxSlKGqI+DB\nD40sK95YXQm57777OHjwIDMzM2RZxuzsLM8++yzHjx/HGLOloAYb5bNaa9rtNk899RQf+9jHeOqp\np66p7DiuzwLZdZ/ou9Cccw2s5eDBgxsSyL5+6hKm7KNUQZoavMvRcYmKDEWR4XVJ1HJEESQdgU5B\nRBarLFFbEnc8KhaoWKDbgbnEEyjubG+wJmWLA/75oj+AKwzIuUCng+d6MiHx1UsqpgmkEWB7Qxj/\nfHAsY8oAJ7Bg+wXSeZQLHRklwrNLJREqUkgCj6yKDUqH8FsKQZTEbNu9hziOOX78OD/+8Y85evQo\nCwsLY6ubrmQbrYx5vaaUQkrJb/zGb7B7926++c1v8tBDD635ns301y1Tma2JuMfBNq61OM5d6mGM\noZ1CqcOClLZASYWUnokpjbMOJT1eWvo9R2ciolsa+t0iSOLlYI0JE8wmYG+MCByWE1OabMHgrCVt\nVQTpvqAzEXTclYJ2J0FIuOfeQStnIzCz67Uau1cvEP1+n3PnztHr9XjmmWeYnp5u6L/GxaxtBjj9\nes1aS5Ik9Pt9tm/fzoEDB9bc/8yZM0sU32ZmZq6qvP7sZz/j1KlT/Oqv/ipf+cpXNuQ83602MzMT\nJBxH/N2vBQt68egbJEkQJhCAUpI4kohYEseVVGQFMVCxQEuBUR6pA6xARh5TYeN63RyQxCnkRZCr\nzavKT8DZKdIJQbkYeotFt3rNFOF9bYHvh8XV2hKlfPBrGQLkwX3qiFTAOygKhAgP+DiNeORDD1zX\n9V2Jtm5ubo6zZ8/y05/+lHa73eDj38nEaqN8f/kwyX0jtGvH9dmfVxNCcNNNNzE/P79heOT//S+f\nIesHnmFTNs0OlBJoPM5APwOX03DKTk3F5P2CLAeZQlTFk52dCWUvxwPtbTFmIagd6FQF7LwQRLGk\nzB1ChmG2rG+ROgxf5QUQBciBQ+HiMDCWtCNMEVTHWu24YVdI0whfeIy2xJFGuApikGisyxESRCTx\n1iKtx1uLwYGSSAlF7yJoiVSSKAry2aYsmLnjzoa2rpZ1P3HiBJcvX+b8+fNjzbMst40sGG3UPTB8\nTnv27GHv3r1r7r+Z/rplgtldu3Zx6tSpsbWjV1sc//Lf/JS5uXnaLYn3kPccExOKrGspetCZjOgv\nltg4UHWYvqUoDFLZMOARSay39LswtS2ldyWn1xWk7ahyRomMBHleIoTE4fHeMjnZot/L0bGk1Yno\ndhf5yC98ANhaE8gQsHt79+5lfn6eBx54oKH/eu2115a0AEeh1Hk7wOnj2riEzislF8Pf2znHb//2\nb/MXf/EXG3J+72Zrt9tMTk4yPz8/8nvWGgDr9QpeeukU3kO7pShzS9pS9BfD/mUm8NYzvSOh18uJ\nW4p+hamTMnRL4rbC9AwyqgbFhCAvFGWFaS+6JVGsKI0FKbBOY6RlaqpFv9sn0pqiCK/FMqJUhjQN\nCbNAUpZlUAYyHmdKdKRx3iK1od8zCCWJlMaYgnYnQeqNbXxprdm9ezenTp3ioYceaiAJNX5tHEqd\njbSN8tnaXxcXF0cuatzw2dFMCMGePXt49dVXr73zMlstiPr+fznebEdaQCVGkrY0pqqgJi0FVXdF\nKMi7AzYTPfQoCPRY1bkSsOtU+FXbD8Im8aTGXAn76Y5A5HX5FcSQ0pezIGrxFDJk9ZGFyNGV4IrT\nClnR/Dml8dKgdEQS6YCTFx4dKQrACREUwKwH7VDa4gwoqmePH6h+feCRR5vvUcu61xChHTt2MDc3\n18x0jMvssdW6n+uxzfTXLRPMwnhUW9fa///++xcDrKBwTE+nLMxnlIUlbWvynsE7T5wGHruoIykx\nFD3DxHRM/0qJyR1JS2EyhylKtA5tSoFDeo/tW9KpmDz3WOtotWOyvqMsDWk7Ist7OGuJE81jBz4I\nDKqxdTVrK7QK6/NZD3ZvpeNcr22GNOao1FwzMzMNIwTA6dOnl1DCLSwsMDs7y8c+9jEgQGOAfy+E\neOLnbaBkHHnM2taqzP7lv3mmUa70tu5ehP8nqcYWFeygqqwoFdADQg+UgbyxCC9IUkV2JbQrTdcg\nEbiiRBhP1AGzGCR8ioWwv8n7iNyiNA2vZeEKpBM4Dc6V6M4kZR5kLW1pgqJQJCjzPmkUkxehIlWW\nGYiSex788JhXdHSrE8dOp0On02m4qIcpdaIoWkL/tdw3N/LZs5GJ7Li80OP6LPARfk59dj02vGYN\nW1EYzr4xSGRjrSjsUrU+CL6bV8GsD42M8PeOhsqX0eAy28iYlN1ywFyQDcEKho4bJRJTiZm1JmPs\nQohYdSoR1cAokiWCCLIcvD8MnlXb3qCcRZQFpXZIJxDeY/pF6MgIUElEaTJE2UPoFCElXgh0FGFN\niVCS1uTUqmqIAHEcL5npGKaZtNY2wkirydlvNZjBchtl/d9Mf90ywex6FseVKrPee44fP86p01eY\nnIjpdgtMaUhqRR8ZJhPznmFqe0y3sJS5pTMV07sStJfTVtCWjmOFKxxl5pjantC9nGNyQ2siolg0\nSBmgBEVZIiUkbY0pC5SWSOHI85L9t9+85Du+kzCDlWy1c1g+xbiwsMDFixcbydk6sJ2ammp+h60W\nzI5bmT1w4ADHjh3jxIkT7Nu3j29+85t84xvfaF6fnp7mrbcGGuQf+9jH+Md//Mef20VxPf660v5z\nc3N86//8GQBxrDDV4FV9N0WxwBagIzHAy1YDIq1ORLlYgvTYnNXlaqtpakFN6aXwmQN8pfolkCqw\nUkaphIpiyBqDUB5XdHGuJJ6YIstKpHBkvcXwnUobBj8SSZ71Aqf0L29uMLvc15Yze2RZ1ois9Pt9\npqamGp/VWm9Y8rmRVsMMxuGFHtdnhRBPA//Tz6vPjmu1jw8/k4ui4H/7k//UTHhJCcVQZdUWQ7jU\nYvD3ThpheiE4dbgmmGxPVrACgl82FVcNLvcDtoKeabbLrGi2rR18howHjCVRWyKqSqxuaWR1LkKB\nGlLfVc4ipEcnGlf2cFoSxy28CwGvsFAUPSAPdGVlCVqDEDhrEVKCgJn33bXiNVzJ15bTTNaSs8Ny\n9jUkoWb22IoFo3FjmM301y0TzML6KrNlObgray342Rcv4VyOx5EkDmeLUGWVQaVnYlrgjMeYApVa\nsjInabdAlSz2CqanWxRlSa9v6EzG9BZz8tyhUzClJxUSdMisprelGNOjuwjtiRhTZigdkaaavMj5\n6C98sDm/rRLALrdrOcgwdu+OO+6gLEsuXbrEm2++ycsvv0yr1UIptSG4vc2qzK7GVTxsWmu+/vWv\n8/GPfxxrLZ/73Oe47777eOqpp3jsscd44oknNuS83it2vZ2UYS34s2/0AEhiSWYsUTwIXGspW6kD\nFZdUg2qskmHt6kzGFAvhWVBUQx7e1wNiEaZngAE2tq7sxi2Nrz6nyEokAlVx3Mo4LFJSgCkNSIvJ\nF8AE+UoPiKrN6ITFGROGLqcm33Fe4zRN2bdvH/v27cM5x5UrV5pOi5SSbdu2Ya3dkAVyo4Li9Yic\n3PDZ0W09v1Pts7UYTy1T/cxPLjf7dNpRM5iVpBKGKqMmDxVXBLhhKIEdHoQe/F1HPmBsgXRCk1XM\nBXFHQDZgIRDFYKhLFAM+W2kHXLNaMeCdTVXT3UkmY1xdyW3rMNipHDqSmLIEXyKVwgowwhPJAp/3\nII7RaYqvKrFKyqAWRui03vvoYytew1F8bLnkbK/XY25ubomcfZ7nGzKsvdGV2bIsRxZr2kx/3VLB\n7DAJ+yimlCLLwrBbrRZ255138qd//lLAvxKYCbJeQRzFWFuycKlgcjqusDueyekE27csXu7SamuK\nrqXo9VHC4jJwsQdrKLolE1MJ/X4eVL86EXlRkGcuUHIVBucccaIxhSFJBUIY/sW//MiK575VAtv1\nLGZRFLF79252796N955er8eJEyc4f/48Fy9evC7s3mZUZsfhwHv88cd5/PHHl/zty1/+8or7fv/7\n3wf4ua3wXE8npVYL01oj9c046xBi0EqMY0XRD1RZppKdjbSiKAztyYj8SgkiKPAJRBO4xhOKYtHi\n8ZR9X1Vcw+fHbYWvjlUfM04leW5DNbZehKuqb9oOz6M4iQCLtR4hPGk7RSmJF6AjjfclCIdUklhr\n7r53oP6zWTaOz9bB67Zt27jzzjspioK33nqLoih45plnrluVa6OeY3UFcFxpzHF81nv/ses5x/eC\njfPMH/bZU6dOcfr0aR555BHOnX+OiYnQUhfSI7TAuSBaknaiMO0fCUxeYq1DSEAKtJYoBcIEH/WA\n6dqGksvmg+2ayQRAqkGQmnQUZsE0275fKYBFYLOhY/UG23mvh674bPOsT1RtG3IwBmGhUMJ0AAAg\nAElEQVQcpRFBQUU4ysUMQY5XEtFugRRI71ASfKIRPkAOldTISNGZmqQ9QsFkVKvl7Gsu6suXL3Ph\nwgVmZ2ebYfmdO3fS6XTWNUi2kXCFcUWJNstft0wwez0wgzfeeIMTJ040amHPvXCWJNGUhWGioykk\ndLsF7bYiLy1FZmhNxGSLBda4Cn/jkVKAhCIzdCYjssWSrFvS6mjKnqXIS6JE4UuHEJ440mFx7Wic\nCpy17YmYnu1S5JadO6dJ0wGGRgjB4uIiR48eDUwLW0CV6noXIiEEnU6nwdvu2bNnLOzectuMYHYr\nSO2912w9/lr/9jXX5f79+9m3bx+/+dt/Q5HPo5Sg1/UgIM80+JAcCgtSCXAlUeLJ8wKZQJRGFL0g\nDZTnEqcgkTFWl0x0YvKsQApBXhi8hqSV0rMFSapxuUUoQdbPQYJONaWxYQGuFsQsz8BblJaUZU6c\nSIqyRJQ+CHJIEKKkKPrEcYQxHqkkH/1vPrpZl31DLI5jdu3axblz53j44Ycb7N7hw4dxzjVc1Kth\n9zbLhjGzN91009v2uT9PVvvsqNPsdfezhpcdPHiQV0/M8cabC02bvxWH4ShBmCvJq27pxHSMKxwC\nmJ6OyeYLHKAmREWdJYjaQBHCWtUSeONxyqOUpMhLiALtVWE8Ig44+tI5XFLxtUcC40Jg25pIKPqh\nrNtqRQ3+PU1iXGGx2hIpjfDgZFhnpAtJtE4V1pUIFZ41Ho/wAmFdqNYqCUJgTBmw/EqgVYzzDpN3\nufWm21a9htfb/ZBSsmPHDlqtFg8++CDW2kZopNvtLoEQjVIhXQ4bWa/Va/U4GPfNtC0TzMLVsIFr\nmZSSCxcusLi42JC2nz9/hbI0tFuSsnLciYkoTCl7T6ujKy5KC8KT90o6UxFlXpJ1C9odTd61mMIQ\nxQJvggqRq1qbne0RvfkCKT1pGuO8AiydiRjnSoQvmZhoA55HD9zTnKv3nvPnzzM/P8/dd9/NyZMn\nee211zhz5kyDdRuFNWAzbKNwOEqpkbB7O3fuXFW6czNgBlmWjaUodMNGs3GDWQgtqeeee44HHniA\nqakpAJ7+UZiw1lriTDUIYi3eebSALC+IW5psMSxUSSVO0orBFCVxJ8L3cyTQNwXCemQbRJahU43v\nhsWuTwalQyUxLi+QkcRXJO9FN1Rmo3aKyTNQAusEWkVYU/H2eIdwPgTW3uMoK6ogGRJr70haKTtv\nWpsbdStYXZ1ZCbt36dKlNbF7y20jYQZa6xvy05tkwwnoqMGsc47Z2VluvfXWRsjoL/762SaQlYoG\nox5FITiszQwNb9WUWABlYZrKqI4ErhrSaqUDtoKoJWExnKNOBDIfwApwHuXDa9ZblA3bpSxQRdj2\nkUUVqto2aBO2ZSKQWbWtBSiLkx4dhWeC8I5ICoyzOClQFYuB0AKZaLAOtEIJiXUlnhyl4N6Dv7jq\nNdxoQaHV5llOnz4NwPbt29m5cydTU1Mrfu5Gs4+MAwvaTNtywWwNG7iW5XnOyy+/jBCChx9+uPnR\n/vwv/yuXL11ishOyprxvmJpOsUWOzT0TUzG5KegvQNKKKPsFeRYRJR5bWoRPQBYUhWdyMqFvCrpd\nweRkyuJCnywrSSciFhevMDnVwrmSosjoiISiNOA9E1Mt+v1F/uXHA8SgfiAURcHNN9/M1NQUExMT\nTE5Osn37di5dutSwBtTBXj2osdm22brRK2H3Ll682Eh31hllzbu3GZVZ7/3bSlX082LjwIK897zy\nyisURcFHP/rRZup3YTFjbi6IqMexIjOWTich74XANeuFlS7SkiKHdCLG9sPf6n2iSFL0Q5XGl7Xq\nV9gnaWnywqAiia9gBPVQSjoRU1zJKtWvUEEyVTIdtSOMyZE4rNdIUYk0SEGcxuTFIpEKJEJChMEx\nIQT771i9QrOVbDW/11pz0003NZXRlbB7O3bsYPv27Y1vbZRZa4njeMssju9FGycBvXjxIhcvXuSu\nu+5i//79zd//6QcDSq5YS3wFy0lSTVn5XdwCV9aqXwNYj5AQDal+2dwNYAFDsAIhh0QQOpJyvhJD\nmVK4bqUSOCRHKyQDBTBohsCAwCdfBc/WFcgq7PHaIitmWmsM0oeukHcWaSxeCWSagHUIL9BKYSu/\nUZHCmCtIKYjiFtt3Xlvt73ptpSHr1eZZzp49y9GjR+l0Ok0yWkOINmrNH3fAerNtywWzozja5cuX\nOXz4MLfeeivz8/NLfpj//A9HUVKQZQXT21osFIa8V5C0NXk34HeStsb0DVoJjBCUWcnUtoRubugt\n5qRtFXhn+wVJLLG5xQxJ24ppgVaQdbt0phKKEoqiJG0lZFkfU/aZ+P/Ze7MgubLzvvN3zrl7ZtaC\ntbuBBpog2Hs3G0CD2saiJFIWRXvokUcO8WUcEkMRfpAcepJMhxwejRSSIjQRfrLC4xjSlGWOTMoW\nR9SMLM6QpsWxZKmbW2NpoNEAuhtrY61CVWbe7ZxzzzzcpRYUgKpCNVlN4XtBovLmrZtZ98vzne/7\nL4OYnQ9vpygKXnnlFXbu3MmWLVs4fvw4V69exTnHtm3bOi/2bdu2IYRgOBx2xW1b7G3btm1d2Jjv\nZKwmQRZj96BmxS7X3VNK3VXe5EFsnljt/ViWJceOHWMwqIlRi/++v/fv/lsnydUWmS3+1Q8VNMoG\nVbvQtcLsgcI1z+mmuPUjj1LrJYWrblx+usJVQVU0hWtb1A5CzLBovN7rznDNkHYoT1KaMX6cNJJh\nDl2OwGmUijHNF3pVWaSSPPKe3R1ufDNvoFarPrISdm9mZoY333wTz/OYnp6mqqoNWSDb6c5aMbMP\nYvWxmjW2tQK/ceMGDz300JKNRVkarlwZdv/3laRsitk8y7qiMQo9ct10Y6WFpoANYgmNwoAMQDRO\nC0KCyxc2RmW2cI26MCxSkV343ZHsCmAvBpE3zn4hSNsUuQq8RUWusguPRWURsjZSMlYjPFebH0kH\nnkDaehrjvLpDa0uN8CSV0FidAbXF7Y5H33PPz3Oj1u57nWc5n2U8HjMzM8PJkyc7Leo8zzdUFWE4\nHG6KfN00xexqMHjOOS5cuMDly5c5ePAg1lpmZ2eXHHP+wgyDQchomKNLTRR76NwQCw+loMwMk1si\nRplB54b+ZMjoVoHVFWHsYXNLGHg4XVFpRzTwa/mu3NAbhOTzBc5VJL2APCuobIXvK6yxCFEhRUVZ\nGp59/+MMh0OOHj3K448/zvT0NNZaJiYmKMuSrVu3cvnyZdI0ZXJykunpaSYnJ+n3+0u0I2dnZ5dg\nY7Zt23bHEf16YiPlPtZ6npV099566y1mZ2eZmZnZUOzeZt4IfC/H/Pw8x44dY//+/ezcuXOJ7ArA\nn/0/tYVtkvhd4WmbIjWKPPKRxW9IlfWTrSSXopizCI+uG2ubxbM3EZDdylGeoGqE0UVTIPcnIvK5\nthvbEEeEwADxRIQZ1ZOhIFAgBEJYPE8iXYGTEmczbGWRSmGMaY6piSzK83j62aeYmZnh7NmzhGHY\nQW42G8xlPSSQxZMUqKdj169f74hka8XuLY/NNrb8Xox7rbHGGI4dO0YURbz44ou88cYbS47/3H88\n0j0OfNFJcglBV8hCzTtpIwl9TAPnsc6gGlEuP6pwaf047AvcuM5jGdIpGgjlcPmiUja30Ly+0gtF\nrueLzrbWD4FaHKUucrPmmFh2BgpCCcLQA69CitrhC+kQrkI4i/B9hKihTtLzEJ4EBw6NcPXvbY0S\nnj70g3f9zL+TxezyY/v9Pv1+nz179nRa1NeuXePo0aO3QYjWeo0POrN3ibslmrWWEydOIITg8OHD\n3Q5+8fF/+mev4Hl1R6XXV0hRW9UFvsLza0yeq1xN5uhBmeeETiB8S1ZoJiYi0tIyHqckiU8+MmRp\nRpR4lJnBaI30IMszlBeCqMjzlKgXYE1BnpXESYwxmsPf/wzHjh3jueeeI47jDkS/ZcsWnnzySYQQ\nS7odN2/e5Pz583ie143xoijq5Draru3MzAznzp3ruj5BENxXsmzUmPB+GZItdq/F++zYseM27F4L\nv1hrYbAZVCO+F+Ne99ylS5c4f/48L7zwQlecLBdhP39hBs9XCCBOAjxf1KO/yFE5hwolypeESYRU\nAuEMnu+jjcaqisnJmHQ+QymJ1nWnxZgSJxxR36eYLxFCkA8LBKLD5AeJpGoW2HyYIRAYUxeyfs8j\nz1N8T6CdRgiLkhJjcuIkRmuL9D0qLFLUVtbgeOTRHUxOTjI5Ocl73vMeiqJgdnaW119/nbIs6fV6\nnbzOd8uBp42NWGDDMGTHjh3cuHGD97///Z3812qxe8tjrSYnD2Jtca+G0Wg04ujRozz22GOdkP1y\nLfc//9pr9PuSvNBoo/GVwg88olAhKzDGUuHQ1qGkRMqa1CUkOAdyocbtzFEApHC0V+VHgqLByEZ9\niR212tBAudBxrboi16HHFtlCCUqNot5MWVMiqSdBlrx7TKApdIE0EPgK60rAEiYeVaqpLPi9qIYf\nVbUbmC6HKOsQnqqNjzxBkITs3P3ugBa1fJaWSKa1ZmZmhjNnzpDneedWttpm2Xp0od/JeFcUs2ma\ncvToUXbt2rXE13e5o9C//8O/YjzKMKEiCj1G44KJQYhzFXNDTdL3MIWFqmIwGaGNYTQ7IkoUOrMU\no5TQl5jcYDyLwGJyiIIAbEkxdiQTEXmek+eaXj9kNC7Rha3VE7TGOYOjJOn7PPfcc13R3cqG7dix\nFFuzvNuR5zk3btzgrbfeIssypqammJ6eZjAYdLusvXv3orXm9ddfrzU6L1++J7HqbrGR4PT7jdY3\nejF2zzlHlmXcvHnzjti9O8VGMTcfxOqjqipee+01yrLk8OHDS+7H1jjB8zz+61+eZtyQuqJAkqUl\n/X5AmhZIJVCNemQcONK5nGTCQ49r9y0Ph0JiS4OykqjnkzdyXeWoViNwtsbjRf3a5hYBOtP1wiqg\nUhXJIKQc50gpMboCVUuACetQnqLS9UJmdA44Kmtrn3jfwxiD53lU1uAomNgyyalTp7qJQhAE7Ny5\ns8v5q1evMj8/zze+8Y1uc7Z169YN0Wdea2y0ALsQYkkhvxrs3vJYj5Teg1hb3GmNvXLlCm+88QbP\nPfccg8Hgjsf/1V+f7Lqx/Z5PmecUGrAL0naDqZBiVGCBcOBTNkYJQSSpiqpurEooU1djYwVkY6hF\nY8EVEqOaYhYf49W5m0QRuSsQQBIHnW5srxej8xKnBFEY4kyF9Up8z4cKrDQoqaBy1AJfAipTb45l\nhXEFQtQKRdYUtVMZ1J1YWeeJKYYIYXCivtcrV4HJGUwtmCLdKTabQUmbs3Ec35PPcjdiegsLetCZ\nXSFWSrQbN25w6tQpnnnmmQ5r2cZyR6Fjxy8QxfW40usFeEqQjgsGkyFlDjq3xA12tsw1XqiwRSPR\noSxWV8SJj8kNOjPEA59ybMjTkrBX42wrZ0jigKIsqKzC9yRWG8LIB1ORZSMe3rWDF154AYDZ2VlO\nnz7NM888s+RL4k4RRRG7d+/uurazs7Ndcdv6O09NTRFFEb7v89BDD7Fly5YOa9t2bduF8l5yWBvN\ntLzfWKlrJYTosHuPPvroiti9tmvbYovbbuxm2TV+r8Zy0l6e5xw5coQdO3bw1FNPrehU1Raz/8fn\n/hoAT0mKhvncGiQMJiLS+ax26xsvfS7qedixwQmHbkwQ2r93PPAw48YgIW2cvWTt/RX2PKrUgIMq\nd8hK4myFsgo/UNDADnRegKg1ZYVwNaPZVQgcpjRI36tNFJpOl9YpSgn+/sd/hizLuHHjBq+99hrW\n2i5f+/0+YRgyGAx44oknyPOc2dlZXnvtNbTWXRfzOyWH9U5bY94Ju3fixAmMMV3BPzU11b3+Aczg\nnY/la2xVVZw+fZrRaMThw4dvg4csPv7Yq5eWwAoWu3vVG8c6FnG3MKbsCF5OOYQGKogGHtXYgK2d\numRRu/GhwI5MQ8wCXeXIqn6cM+wIX1bqDiNrvBKRNb8w0NBCi3oLbmLOF0hdUTUFsxQWJ0MC369x\nt1KgnEAUBus5fNvg7n2oRIq0rlY/kOCwUKUIJXjf85tbhm+lWGmKeic+S0tM7/f73Rrbch42W75u\nmmJ2+QiktaWdmZnhxRdfXHE3v/j4i5dvUhSaOFJQWYq8JO75ZKMSayxxL6BMy5pcIh22rIj7iqyo\n5bkGkwHZfInOdS3KPtbgHEGoqEpLEHhUpcVXAj9QeEFti5skEcYYpDREoQSp+PGf/BGgHrNeuXKF\nAwcOrEuMvCWHtTJXaZp2MlfD4RClFNu3b6eqqiXyOm1XpJXDmpyc7Lq2y7uY303M7J3Oc69FdiXs\n3s2bN5dgi9ukXMuu8Utf+hK/9Eu/hLWWn//5n+eTn/zkkuf/xb/4F3zqU5/qusb/5t/8G/bufXeM\nmN6pWGx32RINnnzyye6eXR6Lx5YvvfxmfQ5PUBlQUlA1eFlr6gJWetRWXWIBG6sEWGpnLzs2OBzF\nSAMCJWv8a9j368IVR5lqBDVpswT8REHrLpbVC6fya8tc6deLnfIkhS6R1LI8zgqiOKa0FukpnKjF\n4iFHCNi2cydKqW568thjj3VjvGvXrnXa0nv27EFrTRiGS/Dic3NzHaQmjuMu79fzvbGa+E5aY66E\n3Ws36WfOnCEMQ7Zs2YLWGiHEmhbHtebsV7/61b3OuXPrfsPv4li+xpZlyZEjR5ienubgwYMr3g9S\nyg6a83u//xeLTgZVVedjGHpgGvImjnSYdwWscpKqmbBUxiIbvKuUS925WrWDsO9TzDVuYn0FDRFM\neCC06H43xcKlVGXVndfockG5AIPHAimsxc6qRIIrqWwGwqdSFUJa/NhHZwXKCkTk1Rdoc7wgpDIa\nISSe72H1ECEFUipuZfDNb36z62K2qjyLY7N1ZlcTK/FZFtvZT09P45zr1EdWqwv9TubrpilmYUHq\np7Wl7fV6HDp06I5flotvkP/9U19hNJzDVQFSQJGVhH6MkiX5OKM/ESFEQTZ2xIlPkWvK0iPsexRp\nhrWgQkuhc6JeAl5JnlVMTvUZmYJxqukNYoajEUkS4PmSNMtJ4oBSpxRFRdJP0Drlb33w+3j99dfR\nWnPw4MENYzUnSUIYhszOznYd2Zs3b/Lmm28Sx3HX7fB9n+3bt3dY2/n5eWZnZ3nzzTc7E4ONJqVs\ntKvIWiIMQx555BEeeeQRnHPMz89z/fp1xuMxP/uzP0uaprz00ku8+OKLd/xbWGv5hV/4Bb785S+z\ne/duDh8+zMc+9jGefvrp7pgDBw7wjW98gyRJ+Ff/6l/xK7/yK3z+85+/r/f7bo52cTTGcOnSJa5e\nvcqhQ4fuOjZvF9OZmRG35mqWRhJ7jIaWKFGUDRygyOrF0fME1jjCRFLlVQMVaNx/fEkKxIOAvLGy\nNUUrlK5IU0PU97HN8bZsJYQURWHxQoloRqPtyNKLFUYXhGFAnpcID3ReM5cREoElDEMKXSDQaC0Q\nUvDswYO3vVff99m5cyfGGMbjMU8//TRzc3OcPHkS51wHk0mShKmpKSYnJ2t8b9O1XdzF3Oiu7XfT\n510ptcS6M8syZmZmGI/H/Oqv/iqzs7N8+ctf5sd//MfvOs1aT85+9atf/R3gZ9b1Zr8Hos3X1pb2\n8ccfv2shsrj4/cu/OtP9PApUpySg/ArbNGnDWHSyWEI5quaxVOCZhfukJWYCCLMAFVx8S4aRomiK\n2SARuIbUFQ0WtKNVKJDteSVd5xZAaEeLqq2MWcDUViVKOKSwaDtEOIWsHCbPas1ZKxFKYhnjQ0MO\nE1g05GOEEggpmd6xi+eff76boF64cIHRaMRgMOi6mL7vvyuL2cWxWIv6scce67Soz58/z9GjR/kP\n/+E/cODAAT784Q/ftbnzTufrpipm213g17/+dfbt28dDDz206tf+lz8/jlKCPCuZnk7QZUmeFk13\n1mK1wQ8UJjdIUY9CdG6IIoWuIJ9P6U9GZLkjnR8TRj4mteTjFF8KXGGowoLQd2T5iMmoD64ky3Ki\nOEDriqJI2b5jC6dOnWJycpInnnhiQ2/idif98MMPs3v3boAOU5qmKTdu3OD111/vFsCpqSkGg0Gn\nQ/fYY49RliWzs7Md6LstPNqRwXrju7k4Lo4WuxeGIWma8s/+2T/jt3/7t/nX//pfs2XLFt73vvet\n+LqXX36Z/fv3s2/fPgA+/vGP88UvfnFJov3oj/5o9/j7v//7+exnP7vu6/xeCSEEJ06cIIoiDh8+\nvKquelVV/MEfvsSg71NVFVWlCUJBZQ3KhyCQtUeBA6oK5QmCQJLnFj8UuIaRnA4L6plhvRiqwHUF\nazqfARLl1cQSL6w1LQHyUYZA1T/TddenXYDLcozA4pxEyIoo8tF5UetKFgXK8xDSIV2BkgohBUEU\n8gM/8sHb3murrZumKYcOHergP/v27UNrzc2bN3n77bcZDocd+bGFEC0WRW8ZyG3XVmtNURT31bX9\nTk5S7hUtdu/y5cv8xm/8Bh/60If4+te/ztzcHD/3cz93x9etJ2eB3fd1se/yqI2FrvH2229z4MCB\ne06t2nxN05Lr1xYkuZSkI2yVeYlqSglPCUwzQZGeg0Z6K+p72FGzYUxqAyKoC96ygQqBI5srEE2X\nNRumnSZsTeSqIRDOFUAr9QW2uax4sEiDdolyAZ1pAkAcBkivVh5yrsTvRQgUTmdIz8dZi3Aa369x\ntjiHUBYhDFJ5QL2p3vboE90a2jaIWpfP2dnZjgjpnGNycnLVRMjNHm3XNE1TPvKRj3TOgb/zO7/D\n7/7u797xde90vm6qYvbKlStkWcYP/uAP0l+Dz3FVVVy4eJPBIGI4n1EWmjj2KbN6hfICWVvUTvjY\nopbnGkzGDGczTGHpT4RkwwJcLfxsSlNjYWXtFd2fisjmLVqXJHGASUuKbEwYKsqyAgee77DW8Mju\nh3jkkUfYufPewPC1xHg85tixY7zvfe+7bYTbWsr2er3OxWdmZoYbN25w9uxZkiS5rWu7fft2hBBc\nunSJ69ev861vfavD5LZY27XEZlocYaE4D4KAxx9/nE996lN3Pf7SpUtLyIW7d+/mpZdeuuPxn/70\np/nJn/zJ+77Od3OMx2NmZ2fZu3cv+/fvX9Vr2k7PH3/xr5m7dZMwVORNhycMJNZUxGFMOsoIY0WZ\nNmNGJJWpiLyQIs3xI4XWjTPXWKGpCMIAnWriJMSUJUjIC0klHb2kRzoaEwZ+rSGrBEUpcMqRJAEm\nN0RRiNEOoaAsC5yri1rjLJH0sVXtHJiNZxBS4qvaWCEI/du60dZaXn31VaIo4vnnn78tN1q8ezvG\nm5+f58aNG7z66qsIIbqubTtxabGlWZZx9OhRTpw4gbV2iWLAWvLmuzlJuVvEcYznefzWb/3WqtQy\n1pqzwJ9t0KW+68Jay8WLF8nznB/4gR9YVfOizdc//MJfk/QUo3FWEySFIEoUrqpQwsMYCw6MEUhP\ngYM48NENbCjNxoTUm68gkphm0xn1JVWjVuD3JLqR5/JCkKYpT4RDlAvXWhWO9o6rC+m6yDUmXyh4\nlaYteFUENF5MKoKiHCHKmh9T6pQyzenFfXSVQykIE5+qGIMPQRyj9RDPCITvUVVljcEXFY++7/1L\niNpbtmxhYmKiW4sfffRRjDGcOHGCK1eucO7cue+4KdI7GYvJ2j/1Uz/Fhz/84bse/07n66b5NNvu\nYq/XW1MhC/DHf/LXBIEDDIOBhxQO35cEvo/vNXa0lcQYTdCDIi+xVuJFUJQ5URyDrEjHWa1yUDqy\nUU4YK0xWUWQ5fiQxOsOGEEYSbUo8PyAIJMamhGGIVIrnXniaW7dudYLiG/FFPzs725HgVkMi8zzv\nNvLFclLK9PQ0vV4Pz/OYnJxk3759nZRQ27Wdmppi69atqxKA32yL42Lf6NXg71aS77rTYvrZz36W\nb3zjG3zta1+77+t8N8f8/PwSTPdqolUgeePNqwB4SlFqSxT7mEZnVhd1S6XGpNaYutY0weQ1oSSK\nfYr5AuGJmsQJ2KxEGPCUw+oK6YFLa2JKNppHlOAlCp0ahAKMQyLQokRqAWFFRUESDSjKcT29yTKk\nE3ieh6kMfuDQGqSspbycK3ji2aU6k2VZcvToUR566KFugnK3WKwE8N73vpeyLLlx4wYXL17sMOCt\nDrVSCs/zePbZZzsi5JUrVzh16hS9Xq/7e9zLeGSzTFLuFKu5tvXkLPC/3uelvWujLEv6/T5Jkqx6\nCtd2Zv/oj/6S4dxNACyCtOm+9vo1FwVABTUUqKLGwefzTfdVgm8ExmU4AUUqqRokbVgFlLJx3fNj\ntKr1tpJ+QjHOcAgmJxLytGZ4RWFAZTSV1MRhRGUs1qsI/KBWGlEgpcJqjRD1JtiYkqAppK0o60JY\nVlirkQ6cLqmkRjkfK0ucK3A4hFO4qgBncUI12mKCiozJqUfYu/cx9u6l02+9ceMGb775JmEYMj09\nzfT0NEEQIKVk7969DAaD20yR2nzd7KZIK0XbMHon11jWkK+bppgVQrB//36uXLmy5tf+wef+nOFw\nSBhIkiRkOJ/R74cI4UjnS3r9AGsqKm3oTQQIUzK+pWtYQVYyni+J4jopy0zj+1CVlsrW7BOTO6Lp\nhKpy5FlOlHjgHNpowlChc4vxSuLE529/5MeZnZ3l+vXrvP766yRJ0uHD1jMWfPvtt7lw4QIvvPDC\nuiR8FpMvFpNSrl69yszMDMaYDgezWEpICMHc3NyqBeA32+K4VkHn3bt3c+HChe7/Fy9e7LQWF8dX\nvvIVfvM3f5Ovfe1r7xg5590SjzzyCLOzs6u2x4R6cfzqn5/Atji55gsuinxGhUYq0K2ZARJLhQoE\npoEDtONJUzbOXklINlfUHvHa1VqyzXNJP6wLXgWurJ9rC2Y/VrjMgqRmWAPalOAMThiEcLVZgy6R\nrjmnLcBFCAF+KNHFCKkkf+tDH+neXztB2b9/f4cJXWsEQdBhwFvJnBs3bnDu3DBjyb4AACAASURB\nVDmyLGPHjh1kWUYURd3EpSVOzc7OdiSNdsqy0nhzs+XremI9Obtjx47itgP+hkSSJDz88MNLPrN7\nRduZfe3UpYXzRD5F47qnte5UDCYmItJbNbDVYDoiVhCrTmFAekBebz6dcJhhgXL1GbJbY2QDSyjk\nEFHU4AMdjBCNJrQKXYeBd57ufo4sEYXFQe3YRYUjxwnwnMO4FCEForIoLyTwA6zNQFZIT2CFRjqJ\nsDnWKJAO5wpsIZCewEnq4+wQoQTvfWZBxWCxehDQQf7Onj3L/Pw8YRhircVau4QI2WJPV2OKtJF6\n8BsVi6X03qk1di35ummK2fuJV0+cq4lYRYmSAs9XZGlB0vcQAspMk0yEZNpQZpqoF9TWttrgBbW3\ntFIC0eBoB1MxWWnQRU36Koc5RVkQRpIsH+NEUoPT8xQXxHi+xJQpTx3+gSXkhsVd0ePHj2Ot7axr\n74WfaS0F5+bmOHjw4IaNJFpSSlEUZFnGe9/73o5sspyU0uJ8lgvAF0XRjTfb8edmWxzXKhty+PBh\nTp8+zZtvvsmuXbv43Oc+xx/8wR8sOebb3/42/+gf/SO+9KUv3aYX/Dc11uL13h7/hT/+BgC+ryib\n4jJrOi9x4lGmBs8TlM2iVTWF72Ai6py9bL60GO5P1s9JRU0Ug7pTQy3lZRp92qrxgm8tc6N+gB2V\nDTvbIKVD50NQ9ajUCVC+Qtsxqi2UpcUZixCCuNdjy7aaQNNKbT377LOrmqCsJlrJHKUU169f57nn\nnqMsS86fP3+be2Acx8Rx3BEhF+u8LpfWeaeludYba9GFfpCza4/15OurJy6hywVJrrKR0QNQiE6t\noMzz7udJFGAb8lbt+lWvDfFEgJmvC+Gop6ArUmWHaUeyRK3AZqbD0eq87JQLqsp2El7IhfekFrl+\n+YlCdIW0RFBSkeL501TWIaoKVYESHlqmeLpCOIUVBYEfQGUBgVICo+cQqnb9evb7fuKOn1mSJDzy\nyCPcvHmTPXv2MBgMOhWi5UTtu5kitZvRXq+36SQ0YanJyWqm6e90vm66Yna5buW94urVObKsoN8P\nMGVJOsoJY48itWDr7kw+KmpZEK92HfE8RSk0ZWaY2BIzvpVRZJr+ZEw2l2G1Je6FlFmJEgIv9AhD\niRAVg36vtq1VAqIIKQVB4OFI+Mm/+5El17ZSV/TmzZtcuHCB4XDY7cS2bt26pFitqoqTJ0+ilOKF\nF17Y0PFDS0rJ85yDBw8ipWR6evqepJS2a7tz585OSqiV1omiiCzLKMvyvhUSNhqusNpdo+d5/Mt/\n+S/5iZ/4Cay1fOITn+CZZ57hn//zf86LL77Ixz72MX75l3+Z0WjEP/gH/wCAPXv28Cd/8if3fa3v\n5ljr4iil5MzZqyglkLKisg2ZpBlbNusi0gdXNE4/Db5Ol/UiGCYSPbaAI51vnL3Kxnyhr9AjC8JR\njmtJLleZ7nVVWjXC7HVRi2ue6/s149oXuKpEBj7S1V0cpRyy8pDKw/MdAlC+hy8F73v6OaDG+58/\nf54DBw5suAnCzZs3OX36NO9///u7e7kVOm/Hm+fOneuUSqanpzu5q7Zr2+Kbjx8/jnMO3/eJ4/i+\nF7eNKmbbjtFq8xXWl7NHjhz5E+fcx+77gt+lsZ58/b/+07GFH4iFv1UYym6jiXDo3C5oypaLFAoW\n/TqFo1Wn9QOFzlpJroC0qIvhwVRIOVfns59IREPkUr5YUCsQIMuF+9bpqit47WLlAqfxWoSt32jZ\n2gpTZSAclawI47CGMVUFKo4wNkXi8HwPrS2OHKEtwqsNE3qT2wiCO+d4WZa88sorPProozz88MPA\n3Yna09PTXZ2w2BSpVSDKsozBYIAxpjNqWW9s5OZzrZ3ZdzpfN10xu1i3cjXxJ3/6TYp8iCcDglBQ\nZBlBEBFGYHRGGEX4YUWWjpiYTBgNU9LUkkyEjEdjtJaEfUmZ1wxJFUFeZExO9ShKyzgdkSQR6TjF\n8wRx7JGmKX6g8EOPLB3R6/cQSvLUs0/f9VpXIn1cv36924lt27aNqakpzpw5w7Zt29izZ8+GFrJV\nVXHixAmCIODZZ5+9L1JKKyUkpSTPc44fP87Zs2eXkFLWIyW0UY5di/E8k5OTq3rNRz/6UT760Y8u\n+dmv//qvd4+/8pWv3Pd1fS/FvewxV4qZ2TFvX65ZvpEfkZkcP/apjEEIQZEKpJREYYwRdRdUOYmj\nIs9t7cijErTMGEz0GA3HeMqjKC0ogZAxVtWSebrQKKUoS4uQElBUsiLph+i0REiJTnVNLnOaMk/p\neSF5PiaUFm0sjoogcNjKEfcc+XiEF4ZoneMoeOr5A7zxxhsbPkFpo4UZHTx48DYc7HK95daw4Y03\n3ugw7y0pJUmSTjWgqqpu6vPyyy93XdutW7feJpp/r9jIDm9bdK+FfLqOnP0bW8iuJ1+VUhw/frn7\n/0Q/IBvX1aVUjmafSLJIAs+pisrW94QfSmRT8DrhyOcXTBSKUd4VnelwTEvYqm2l62O8oMI2xWzY\nE9hhU0j3JaT1eb1F8lxCLpUAU3ZhjVPS4Xl+s4nO8f0QnEVKH+kblPGRPnXxW1XgHJ4PQnoIFGCR\nSvHYk4fv+Hm1bqVrIWpfv36dM2fOkCTJEjvZxfKas7OzzMzM8O1vf7szCVqNKdLyeCeK2fF4vGqe\n0zuZr5uqmF2cbKv9Un3lyFt4niTPSvqDsIYKFCW9QUSaGYqsIAg9bFFhyhJPgs1LXKQQVUU2HDM5\nlaC1YXxrnv5ETJZr0rl5fF9RFRobOKJIUhQlDoXyJEaXdbdWSvIs4/Gnn1rze21JH1A7J7399tt8\n61vfwvM88jxnZmZmw0hkxhiOHj3K1q1bVyX0v5yU0hoTrERKaUHuTz31FEqp2wTg23HJajpWG9WZ\nXbxr3LVr132f70GsHLUDlr73gU384X/8b0A9rsyzZpYoHK5yxP0AnRZUlSWdM7jK4UeCqnSEfY+q\nsDgc2ewQ5QRVmSMLSzCQ2NQ0z5UIJxCRQ5Qav+9TNXaaunIIJ6iMBq3xej4u1zigKCqU8rGVAeEI\nAo8aeltgtcRJQ5mVIOoxo6lygsBjmGlm5s8zPT3NtWvX2LZt2z3JV6sJ5xznzp1jdnZ21UVyHMc8\n+uijPProoyuSUlrHrTAMO7jRww8/3LlzHT16FOCuAvDLo2U0329sNmvM79VotdxXG+NxjjYlQQBS\nCawtCSKBMRVlWeIpDyUVvifAq8liXiBrdxJA+naRPJfCdbACkK1clnC4sgUMOGxaNcVjDftr5bl0\nWXaPaycV2ZzLtQMW/J5cwNdGosPDCwXGZFRVRRAorE6pgoIoCsmyW3iVw4s8inQepMD3A4rsBkIJ\nlO/VboBS4EzB89+/sorN3NwcJ06cWDXMaDlRezQa3ZGoHYYhSZJ0MKNbt26tyhRpeWxkMdueK8/z\nDdWsX29sqmIW1jYGMcZw/foc/V7IaJhhTUXSiyjSHFdVBGFtbdvrh2gp0FlJPBGTzmWUWcnEZJ/x\n3KiGFQwiynHNpPQCD6cNKvKoBBitiaKIwmny3JEkPlmao40mCkPKsuCDP/bD9/W+i6LgypUrHDx4\nkMFgsKEksqIoOHLkCHv27FmTdu/iWGxMsJiUcuHCBbIso9/vo3XdCWtt8VoB+NYZajUC8Bsp8eV5\ndRd9MyTa92oopcgXYeXuFf/ffz0BgOfVVrIANP96qpZ79cLGSpJ6ROmo8bVFZggTH5fXhWs+Krtz\nWWrVA5rxZpnWHSAh6v8HPQ+X1yuezkokAqlqrUwv9rAuxfN9dJEDFUZXGFEQBT6lKfBV7W4khECI\nAkHF1PaH2bVrF3v27Omw8UeOHAFg69atbN++/Y6+5ncL51w3hnz/+9+/rsXnbqSU8XiMc479+/dj\nrV1iFd26c128eLGDGi0WgF8e3y3C5oNYXyx24FtN/P5nv8JofgaoC8J2m6aUQNbGrrVBnxW1JisQ\n+KqW6gLy1OI5D6UkUgXkjUZW0kvIhjVOfjCRMB5lOKCfxOiyPqaXJJiyoPJLoiii0iWVrwn8AG1K\npCdRst5MS+UhkGhT4Akf4QROGtoSR4Tte3Z13lcO4SxVbpCqwooKURQIKXGiwvMdunAIIZEIHBbn\nCpJkit5g+rbPqYXcvfDCC+tabxYbE7znPe9ZQtS+desWZVmya9cujDGrNkVaKY/eic6sc27DjKHu\nJzZdMbuWnePn/+OfE4YelauYmEyQAjxf4fsS35PEiY8uJHmeEQ1CslFaW0n2fHReUjmD9CBPcwZT\n9Q2YzackEzGlNui8JOqFFMWIvKjwA7BmjDUxURJSOYfyJIOoxwc/9CPrfs/Xrl3jzTffXJII7Q3Z\n4myuX7++ZhIZLLCrH3/88W4ceb/RklL6/T7z8/Ns27aNKIqWkFLa8WYYhncVgF/JtnMjillrbWec\nsFaptwex+ljr2PLixVreJwg8yqyk1wso87oozRuJH8+XGF0R9XxsoxVtmkI0in2y3BAkPjqtf1am\nRfM6gSnBjz1oCCsmrwXZfV9S5uBHC891RW1jZet5oEuHpxxal1AVIGNwmiDqkxcpnjLo0iKU4MUf\n+jEee+wxgCULUVmWnb3yaDRicnKyw8bf60u/1adNkoTHH398w2BGSZKwZ8+eDu+4a9cuZmdnOXfu\nXEdKaaWE7iYA3z7XFukbTSTbLD7v36ux1vvp//7Tv+oee7IZv0NtFNRMVnqDENtOWRSYhnAlJPhV\nLWlVVZZymKGar4pyNEY13dsyzxC5rTuzUY5s4AoyLhDNVEX6UDUKCmCRZQtpgGabiQOUq3CVxAGi\nqLCmduiz2uIHIVIJtM7BVTgJVhh8p9BkOF+Bq5CmwOgKJwVOOFSgKIt5lCfZvf92p79Lly5x+fLl\nFaFA6412chLHMXNzczz11FOkaboiUftupkjL5TU3sphtv0s2UiHhfmJTFbNrwfRYa/nM7/0paVri\ne5J+XzGcT+n3ApQnmZ9J6Q1CXFVhC0sU+EgnKVPNxHSCzkqyYU5/EJEPc4q0qIvcrEQXBSqUNShc\nVjWbuSwZDCIyU1AUBf1ggC4yitwx8dDWdY/azp8/z/Xr1zl48OCKnY/FOJvF0lr3IpEB3Lp1i5Mn\nT24ou7oNrTWvvPIKu3fv7kDuy0kpb7311m2klKmpKaanpxFCkGVZp6TQYm2NMRuScA86Pe98rBWD\n92//3X/qukKuql/jms6p5wucbb4Um399X2EzjQrBNnJd+TAFBF4g0Gktrt6OGGvZLVlrwZbgR7Lr\n1LZ2tWHiUZRmScHr+ZIgiFHK4fcCpAxASJwNqUxG4PtIKZAYhJDU7GaPD3747674PoMg6DZwVVV1\nZMm2c9JOWZbfl1prjh49yo4dO5aIi29EOOc4e/YsWZbxwgsvIKXsyJxt1/b06dO3kVKWC8DfunWL\n8+fPMxqNmJiYQGu9Id8tD/J188VoNOLU6xe7/8eh10lySblQFPveghsY0kJVb9jCxMM1hamK5IJt\nrQJX1FJ5DlfrPjcgA5Pp7rEuFiQNFmhjdc5XTV0b9H2qUV3wBj0PUTSFsC9RxjUkT5CionI5SThB\n2UxfAhli7BjjSTwnqZyFxtmvMgbhS5wrKfO8lvUSggP/3f+4cE2N4tD8/PyG2ta3MTMzw+uvv84L\nL7zQ5cS+ffu6zfJKRG3f9zvoAtRa4DMzM7zxxhsEQUCv11tTZ/5e0W6ONoNG7qYqZmHBO/pukWUZ\nR44c4dy568Rx3dmRAoLQI89KBpMxUgqKtGQwGTIuSvJRTtQPyYcFZV4Sxj62MLWDiSeojEPFAiME\nVVmRTCZkOqUoCuLIo6gK8iIjCAOMNtjKEkUBRTHm+37wB+56vSuFc45Tp05hreXAgQOrLt7aHVu7\nEK1EItu2bRtpmvLGG2+se+xxt8jznCNHjrBv377bfL3XQkpZbtvZSgl9/etf7wTgt2zZsi5oxYNO\nz3cmVlPMVlXFqVOn+MIX/gu+VyJEjb2THlTWEMaKKFK1VI5wBJ7A8wXajFGJII59xsPavUsbAwpK\nXVEpix/5lKkhCLxaZ1ZUlLkFJMoHW1L/28j+ZKMxEtVY34L0BenwFtJzCGEQUhCHEdYYglhiygLp\nK8xwriGRhVROs+uR1eGwW7WQ6el6NNnmw6lTpyiKgi1btnSTjWPHjvGe97xnwyWknHOcPHkSKeVt\nxM+1klLanJRSMhwOOXPmDG+88QYXLlzo3st6BODXKsD+INYXq/27XLt2jS/8n1+laiBAUi6V5CrS\nBWhRK68HtVVsK8lVmhK/wbUmfY/yVl2BxousbcO+Rzmqj/dCgWiWfqFAlJ24CUIvPJYVtOWYFG7h\ncaOAAuDFEjFurj2WtYtYZRoJr9o5UPkS4wBbIpQHJkN4Cj+O6+6tTfECHycauIEXcvHKLQrrs2XL\nFs6ePYtzjueff37DtZbbae2BAwduW/8Wb5ZXImq3ndg4jpmYmOgmRkVRcPnyZebn53n55ZfXZIr0\nbohNWczebXFsO3meP0Gel/QSD13AeJTRH4Sk2qBzTTIIyYY5WutaS1Y7JA4hwRSWiemItDSUaclg\nKmY8l2FKQ38iIp/PqawliHzCWOL7gigaoE2OUqrB51ikrHUm/87f+3treo/GGI4fP95puK53V7MS\niezGjRscPXqULMt46KGHSNPGnWyDkq2FLTzxxBPdAn23WAsppR1zHjp0qBOAb32f7yYAv1KsVTbk\nQawv7rX5LMuSI0eOsHXrVk6+doEizwlCRWUsvle7f5WAJKRIC6K+TzGv8XyJaDoIslKIwhL0JEVa\n1MXsfL1qmbzAlRV+L6LIclBg88Y8IRdUtsJPQkxW4oUeRteqCeR1V6if9CnzijiOybM5pPTQVUG9\nXHq1dE+gatcvpTC2QIiCZw/90Lo+r+X5MDMzw8WLF7l+/TqTk5MYYyjLcsPGlVVVcfz4cXq9Hvv2\n7btn7qyFlNK6ST388MMkSXKbAPxabDvXKqX3IN6ZaDv4t27d4uvfXNSVjQNMAweKEw/bdECRrpPO\nEwKqfGHtjoSPbXq2ZVbQKhTgFo6pFnq6hInCzNfn9WNwNaSWoCepms2o9EA3knsI0KOyk+QyaY5q\nShpTpPgNwrcixxO2gRjM43kS8DDpCD8EoXykMLhC4Ckf6Ul8ATgf5yxSSfwwYu+TH2Dv3r1cu3aN\nkydP4nkeu3fv7homG9WdvHz5cgdbuBcRfjVE7S1btnRE7YmJCaqqYt++fWsyRbpTbJT60EbEu6qY\nPX/+PJcvX+bQoUN88lf/N8piiJI+QaAwuqAyjqQHVucIfKLEkWUjpqb6jOZGpFlJlAQUWUZRWuKB\nTzZOKUqHlwh0mREjEb4ly0YMpvpk2Zgs0/QHEdl4jB8GJL2INB0REhImPq+dOsWWLVvYvn07k5OT\nd72pWzLWYg26jYowDCmKgiRJePHFF7uu7UaQyKAeWbz66qvrhi3cjZRSFEUHNaiqqhOAXwxdaAXg\nV2PbuR7ZkAextrgXzGB+fr7Da+d5xWhYuwP5nqIwligJyEcZSom6SAU8IbBAmPiUowIkmKweQ9rG\n0jZMAophWW9MG23LdiSZDCLK+bzTpxUIKqsRFfi+rGV+pEPkFRJBkc3jRIUQIVARBhGlzhBoykZ7\nsqoEAkEQehT5GOlJfujH1raBXSlaa9o0TfnABz4AcBuJbNu2batSFVgpWgWTVuZvrXE3Usr8/DxS\nys5BcHFut13bmZmZVdt2rtXk5EGsP+6EdTbGcOzYMZIk4dChQ5w9+/v0+36jVqJx0tb4V8CPahuE\nMFLoTNfnC0RXpwax7KBBKFdrRlOjW026oAlba9M2klxFLY8JtZW1a4pfoRaUC4KexI0aUmdfQtr8\nikigTFPOSPDMoiKwMlhhCPyIshhTSY8widA6I9CKyhm0zZDSx7qcbP4WwpNIJVGewlWGIr/BM4c/\nQhzH3Lp1i8cff5xt27Zx8+ZNzp49S5qmTE1NsX379lWpCtwpzp07x8zMDAcOHFjXOZYTtVuI0/nz\n5zto3/ve9z6cc10RvG/fPvI870yRyrJc0rW9W7G6mfJ1UxWzd1ocWxMBay2HDx9GKcVf/MUr+L5H\nWWgmJ32srl2EBhMxWVEisYRJgLSOIs0IAoXODc4HJUCPCoJJgbAVxThlYjIh1YZ0bp4w8TGZocyH\nxElIOi4wZUkYhWhTYK0iCHzKcsT3/dCHOHToEDMzM1y6dImTJ0/eEcc6HA45fvw4TzzxxIaRsRZ/\nRq+99hpSSp5//vlu3LCcRHbs2DGqqloTiQzqjvipU6eWCLffb7SklF27dnHkyBGiKEJKySuvvHIb\nKaV93Np2zszM3NW2c3GnZ7Mk2/ditKSC5XHlypUO5tLr9fif/5dPAc24shlLOtuMGuMaqy4lFA0J\nzJj6mDDxqFJTd2EahYLW2SsZhJTDepW0jQlC1XSJ40GEHuYIWTuCCQS2eS7qh9imUK6sQShLUaQ4\nLEI6wOB7PkaPEEpitEYIg9W10sbUlp0E4f0bI1y9epVz584tsapeTiI7d+7cmklksNARX4xpv99Y\nDHE6d+4c165dY+fOnZw6deo2UspKAvBvvfUWaZp2XdvFtp2Li9mJiYkNud4HcXssXmMXFylpmnLk\nyBH27t1bu1fNzPH6qde753uRR6UbwiXQwl99FWPyuoUahvUxDoe2dY9UCEEQKXReIgT0JnqUaQoS\n4iShyDLwIIoitC7AFwS+Xzvt+TU23RgDvkQKSakrpFfj1q0DoQQg8HxZw5QQqAhE0ch2xZJK1AAF\nJQWCqjY98TxMOcKICs8YhJRUoiQQPpWoN6+eH2LtCCE0QRgzvWM/3/rWt5bA6xYXjrdu3ergOWEY\nsm3bNrZv375qScoW075eBZPlsRjiNBwOOXr0KLt37+5giS1/ZWJi4p6mSG0t0b6XVnVoM6kFbapi\nFurFsVgE/G47mTt27GDv3r2N3FPJ22/fYDBImJ8bUhYFSRKSjXOstUSJj8k1PSlrXExhmNjar2/e\nwjIxlTCaHaPzWo82H+VUxhJEHlVp8TxF5Vkqa5D4eL6i0CWDQR9jMooiJ0k8TCX4O//D379tNNd2\nRFsC1LZt2/A8j/Pnz/P8889veHFlreXo0aNMTU3x2GOP3Vac3g+JDOD69etdYbLR7kbGGI4cOcJD\nDz3U6cHei5TSdmx3797dQRcuXbq0xLazlQlby87xS1/6Er/0S7+EtZaf//mf55Of/OSS54ui4B/+\nw3/IN7/5TbZu3crnP//5js3+NzU8z1sCM3DOcfr0aUajER/4wAe6++n//fLXAej1IrK0lsDTzahS\nKVFLcgUKV9raZaisF6DaLgGivodNDeDQmaE+qC5qe5MBZdOptY3qgRIODfQmYor5DCFBaFcznmXd\n/Y0HMdLzavF355AiwBQ5CocKBLaSBGGIqVKEcNiqHqU+c2B9EIPFceHCBa5du8aBAwdWHCWul0QG\nd8e0b0S0pJdDhw51i25bfF++fJnRaNSRUlYSgB8Oh52SQtvRtdZ2XeqV/NpXigf5ur5YruXeYrif\nffbZDrL26U9/sTteKjC6JUsqZLWgVlCMs+440RA3BQKsxZlGYUCojqTpTIFq3P5kzyIa+IAXV50K\nglICWkWDwHZwIyfrji1uUcOiMWcwwuIZVeNqPYswst7kWofyPKRwlGWKA5ywWJ3WRLQqr9UPpEG6\nCulH2FIjBDXOwdXmKlsfeYJXXnmFp59+ekUTnuVckXb9OnHiBFrru05u74Zp34ho9W/bxkL72S0m\nagdB0G1GwzBkcnKy68q2RO3XXnsNrXVHNmuL2c2yvm7KYrbtzM7NzXH8+HGefPLJJW4an/v8l4hj\nD9D0+wFSgu8L5ERI4Knahs4TpNmYpBcyHo7JspQoqeWAiiJHBWC0hbi2t8zTgmQQUJSGbJjRm4jJ\n0luMM02vl5COc4p8SNKPKYsxiJoMsf/xJ5dc/2IMy/79+8myjDNnznDjxg3iOOby5curgiOsNtoO\nzK5du1a9CKyWRJYkCW+//TaXLl1aFX5nrdEqIjz66KNL9G/XSkpZbts5MzPDcDjk137t1zDG8O1v\nf5sXX3zxrrtday2/8Au/wJe//GV2797N4cOH+djHPsbTTy+4un36059menqaM2fO8LnPfY5/8k/+\nCZ///Oc39DN5t4WUspNmadn4ExMTHDhwYEmXfHb2Jn5Q4SiJYkEY+lA5bFWhdYEXCoJQIkOvltgq\nSqrKocuyXruEwFER9n3KUW2eUIwbvFxT1EY9DzOuC95smCGQGFNvjINe0+EFivm01qS0KaYY0x8k\nFNmYuNdH6xHgqLIMcHXNXJV4cYK1GukJfvhv//S6P6/WTjrLslUTP1dLIpuamiLLsjVh2td67WfP\nniXPc5577rkl176clDI3N8fNmzc5fvz4baSUwWBwW9f27bff5sSJE/zn//yfKcvynkSwB/m6/mjX\n2NaY49q1a7z44otL4Gf/6c/+cuH4RZJcvX5ENj8GGl3noilsvQXFEalAmdYCAUxadueqyoWpa+26\nWUdrOQ2NxF5TI4d9H9Pg4+NBQNVoSweJB81mWHgCtdj1S8tOxQA01hmicAJTzoOnaihTphG+RnoK\nK0uktQilMGUOwlA5TWVV/R0mINn+/jU1otqp4549e7r1q53c9vv9bo2tHdZWj2lfa7QT1eVE8HsR\ntdvvm1Zes3UGBTp5zfn5eX7zN3+TPM+5ePEiu3fvvuN1fCfydVMVs4tHIJcvX+bcuXMcOHDgtu7D\n5/7wS8zP38JXkl4vYjSXYuOAMPSYm0lJeiFSgskKKt8hnKYcacKpBGdz8lHOxFSP8XxKNswJQh9T\nlJS5JowDyjzDOkHUiymyIc5FKOUodUEQQOUq0vEc79n/2F3fj3OOixcv4pzjh3/4h3HOLbmpB4MB\n27dvv2NH9F7Rqjrs37+fbdu2rfn1cGcS2enTp5mfn0cIwZNPPrnhbMfWcvSc/QAAIABJREFUv/qx\nxx67J3v7bqSUqqq6xOv1ep0A/PXr1/nH//gf89WvfpXf/d3f5YMf/CCf+MQn7vg7Xn75Zfbv38++\nffsA+PjHP84Xv/jFJcn2xS9+kV/7tV8D4Kd/+qf5xV/8RYQQwm0Wob3vQrRfvqPRiKNHj7Jv377b\njDn+6AtfYXbmBgDS1SNE4ULKrMCPfBqrLZRVGG27Lqwfe7iyGVsORUNCjrAmI0wCqqaDktb1J0L2\nMGLMYGJAPh7jeR5FbkAJQhGjZcHERJ9snOL7IbqsYQimrMkpnifR2uFJqJxFOF1ryooa2uCqDN/v\nE8brm6y0dtK+7/Pcc8+te+FaiUR29erVrgu0d+/eDZ/+tEYO1lqeeeaZu167EKIzTrkXKaXt2uZ5\nzoc+9CFeeuklTp8+zS/+4i/ymc985o6/40G+rj88z6MsS86ePYtSasWN/tmzF7rHoa/QTQd1NJxH\nNbjWKPTJm2JWetCqZ8lQQFF/xFFfQaNuIEOg6coiwWZVJ8NV60k3m99ykaNgtVD8ekq0xmIEiU/Z\nFLNB4kGjRxsOQmjw9yJQDaeiwg8UrvIQUuAnAUU+wncCPwzIszGuwcdWVY6QhiCMqapaeUVKxY99\n9H9aN9dk+fo1HA65fv063/rWtzqs7c6dO9d17rvFzZs3OX369Komqsu/U2ZnZ28jareQv5awrbXm\nZ37mZ/j0pz/NJz7xCT7zmc/c0W3zO5Gvm6qYhXrHcOPGDdI05fDhwysWeadef4teLyZPMwSOIPQo\ni5Ioqq1mi7RgYrpHSUE+zkka169slBHEPjY36LLEDz0oLZ4nqbSg0hYvBuMgT4dMbZlGF5DlGXFU\nu36VWhKFHnle8KGf+O/v+D5a8fM4jpcsXCvBEc6dO4fneR3GZjUYlJaM9cwzz2woxiyKInbt2kWe\n5wghavzUzZucOXOGOI67ceF6ExvqccIrr7zCe9/73jUX4fcipfT7faSUjVOT4OrVq3zmM5+5Zwfs\n0qVLS7Q9d+/ezUsvvXTHYzzPY3Jykps3b24FbqzpTXwPhRCi68g+99xzKxID//3nvgRAEDRYOKBq\nHIIGvYhhqfEjrxtlOtPozAYKXZrG6KA+PhvlCARhpMgLTRAvOHvl8yNkJaAqkdrhB4Iqd4CjrEao\nSmDLFFUalC/RriQKE8pyhFI+xXgITtdEEFviBR5dd5YMhGXPe5/j5MmT9xwdLo+12kmvNpRSbN++\nHc/zuHXrFk888QSj0YgjR47gnOs6QOslkUFdyLZ4/KeeemrN57kbKcXzPAaDAVeuXOHpp5/m+PHj\n/NN/+k/5+Mc/ftdzPsjX9UVL/nr11VfZu3fvinrGX/7KS9jOlU+iW+ktQVfIAh1pEyAOfHSDc5di\nofaQakE66/9n783D5CrL9P/PWeucWnpJdzohnX2BkIVOAhEisoRNDRAddQARlS+i4/rDLzi44gwu\nDCLqOOIyDl6gXCKjkhAIGAH3C5XwNStkN3vSSe/VtZz9nN8fp85Jpem9qyFg3381VHXXqUq9573f\n57mf+9ZSKm53SEcTGQUrWyKgZYRXSkRrNpQxOPkTvrNmrohYen0j141E2Cl07ELsXOC4eZTS/w9k\nC8s2EQQPoeiE+nhZwHMD8Ew8WSLoLiCqUpgmpgkQae89G0mWQZY4bdqCEe135RAEIbal7OjoYPbs\n2UiSdNIQWX19PePGjRtRAam1tZV9+/YNK8ihvDsLoYNRxAGi4bDu7m7q6uo4cuQIyWSSp59+ut+/\n+Uqs11OKzDqOw65duxAEgUWLFvV609ywYTumYVGV0bEFgWLBJJlW8RwX27RJpXUK2QKWYaLqEr7l\n4Tlh0lfgBciSgC+Gmc+pTALL9rBNm3R1imK2gGs7JDNJZNVHxCNTlSHAQxR90lVpgsBBViSq9Fou\nWH55r+/Dtm22bNnCxIkT+yy991YRbW1tZceOHdi23e9GGZ24KjmMFSHauIB4kGz8+PG9JpFF1zjY\nITI4UU2uVBu0p2Ri7969NDc38+Mf/5jf/e53LF++nH379jFr1qwB33dP9KZt6u1XR3D5r3n8/e9/\nx7Ztli1b1udNc+Om7UCohXNdUNQTxNUoae4kCTwHlIQSV2bc0iaqqBKu7SEoAkGpsmMVSvKBhIJl\nnhyCYBVCwiurpdSvpAIlj0zHsBARECTAc1ETcpj6JYt4gYNIALIHfkBC1zHNXKih92xESeQt7/gg\nU6fPxfM82tvbB9VlqUScdH+INO1LliwhkUgwfvz4fofIBmuXBSeqyZqmMWvWrBG3QXtKJjo7O9m6\ndSu7du3iQx/6EOl0elCH87H1OjxEFbcZM2b0GcyxevUzpNMqhmHgeBaSIqEoCglNRvDD74Tne3he\nODilKnIpYAgIQr17XHE13di5wLNPyA288oprmaxAK7PnSmRk/EJUfZUQopAxRUCMnAsEkL0T32XR\nPfEd8AMbUQxQVRXXtcKE0EDBMfMgQxBYoYTJ9/CtAjYKEA4Oiwi4bhE8k1kL3jTcj7tX9KZp7zlE\nFtllDWWILMLx48c5ePBgn3r8oSKS/E2dOhXHcdi4cSOe5/HOd76TbDbLLbfcMmDYySuxXk8pMitJ\nElOmTKG1tbXPm+YP738Ex85TLNhouoxZNPDdgFRaxLVtAh+0tIBZLFAzLkPeKmAYFlXVGfLd3diO\ni5aKqqw+akrCtkKHAjkpYlkGiaSEaRZxHIPqmhqMYh4Ej1Q6iWHkcRyZydOm9Frti3xYh9r61zTt\npDJ/NEzRc6NsbW3l8OHDFY3Oi9CfJ+VAQ2TRNfaV4Q6hKH7Lli2ceeaZvYroR4rm5ma6urqYPXs2\nO3fu5Otf/3pcnR2IzE6ePJlDh0601g4fPvwyDXL0nMmTJ+O6LtlsFqCj4m/kNYSamhqSyWSf/+Z/\n33uIYomwJlQZ17KRFXC90NnALRFQSRTxCI3TXQeUhERQsuKiVMVN6AqmY6NoJx4LSqQ4mUpQtMOY\n26BEXP3SczRdxTQdFP2EpCHARVEkfN9BFEVkRcK2DURVxraKiKKIYxfDARAh3EgUVWPq9FAjL0lS\nn12WqFo6fvx4fN+veJx0OY4ePdqnpn0kQ2RAfO2RH3alUSgU2LFjB01NTTz++OOsWLGCd73rXXF0\nbn8YW6/DgyzLTJo0qd/u3zPP/IHubA6ATDoRyu5sCHyFoLR+qmpTWN0FAsAIPIKglPqVVvFNGwQB\nQRFwHY8AIVxfpgeCEPo1Fz2QBURBxDADUEBAxLJFAqWU2heoeEooP0ioCVzfQQhATSVCzWsgoKc1\nPMMNO7S6iliSNwiyEB5M8VHVcP0j+Ci6imd5BIGNktBwnAKC74ZVUM9HlAUkWcbz8wiCjyRJzF/6\nzp4f0bAR8YO5c+dSU1Nz0mMjGSKLcPToUZqbm1m8ePGwU0n7QnSwnTBhArt27aKqqor777+fF154\nYcBI21divZ5yZLauro5jx471+Zw//2UjiYSKbdmkkglsUcAyDBKJFI5lYwQuybSG6AcY2XxY1TFc\nHLOIIol4hkMgi0i+j2uYaNVJBMfDyHajZzR816XY3Um6NoPtFLEtA1WVMS0L13PQkjqW0c1551/y\nsmuLJv5GGh/b10YZadamT58+6AjRwcLzPDZv3jxoT8qBhsjq6uoYP348yWQyznjfunXrqETrQtii\nOHbsGI2NjVxzzTV86UtfYsWKFYP+/aVLl7J792727dtHY2MjjzzyCA8//PBJz1m5ciU//vGPWbZs\nGb/85S+55JJL+PnPf/4PXemJrKJ83++1LfbQQ2vIVOm4rovr2MiqiOe6yIqInlRxzVD36pQIaOBG\npukSTs4DMcAxQ5/ZSKKgJEQcx0OQwC5p5Yx8HhBQVAHbBEkVcI3wsWJ3dyn1q5QIlpCwrG4UWaCQ\ntxCEMM8dwUMSRAJBQFbAc4uIsowkiciKxhkLlvb6GfTVZdm6dSu5XI6JEyfGg3KVHPCIPCkHE6U5\nlCEyURRjh5S6urphedQOhEKhwJYtW5g3bx5f+tKXkCSJ++67b9CWRGPrdXjIZDJomtZnpGnzsdaY\nyAKxnR0QRryWfg7cE7rWpJbAL7kPuJ6NFABBQCql4WTDg6yaEqHoAUFYiS2G8h9ZExCLpSEyyTu5\nquvZCH4p2tY3EEtDXo5QQLLDny05j2yHrgleQgA3ICBASsgg+CUXEhNBCImt71l4goUSiIiySGA5\nCJKAlEgQeA5+4CK4VpgUJgjUjJ9esVCAofq09zZEFhW4yofIokPsoUOHaG1tZdGiRRWfcfF9ny1b\ntjBu3Dh27NjBXXfdxVNPPUVDQwMXXnjhgL//SqzXU4rMQv+hCfl8kePHWqmuTpO1bQzDQE8lMPMG\nruOip8KhElEIbX58xyNdlaJg57FNh3SVjuEYeJZDpiZNoSuPZ7uhfU/eQABkTQHRxnEsdE3DcUzU\nhI7iSdhWgbSSRJQkrnrHu0+6tmPHjsWl/UraV0Uam+bmZsaNG8eMGTNob29nx44dWJYVk8aRuCNE\nrgLD9aTsb4jMNE2SySTZbJampqZRI7LHjx9n0qRJ/PM//zNf+cpXeOtb3zqkvyHLMvfddx9vfvOb\n8TyPm266ifnz5/PFL36Rc845h5UrV/KBD3yA9773vcyePZtx48bxyCOPVPy9vNZQPrTZ2w10zZpf\nke04TkJTsZzQa1IRBdwgQFWTWGaRTG0aM5sPNXJGqHX2HBXHN0inU1jFIpIshdY9ooDva7iiRU11\nFYVcDlVRcTwbJAHbsXFlj3QmfEzXkziWSSB5WJYNokBCl3HNAFlRcG0LRQ6rsQIengCeb5AQk9ie\nhyTKmEY3gmiz+LwrBvWZaJqGpmkEQcC5555LsVjstcsy3MpJJTwp+xoi27lzJ7quxxZZo0lk58+f\nz7//+7+jKArf/va3h/Q+xtbr8BCt175S++6//xfxz5Ik4JW6ImpCgRKBDYQAq2jFpBPnBDEWyrZu\nsaxapyUUzNLBU1Yl3NLPWlLFLhHeZJWO213q4mRUAiOUJcgJCTG6XBFE58Q+J5bNiuGEMdYCAm5g\nInlhN8U2i4iShAg4bhFR8PHEgCDbjiiH95sAF49i6ZrF0mcF6YZFHDx4cNCzLH1hpD7tvQ2RtbW1\nsXHjxvjf1Pf9YYct9IdyIrt9+3b+4z/+g7Vr1w4pdvuVWK/CEAc7R/1Ua5omf/3rX1m2bNnLHvv+\n93/K177+3yhy6CEnEpQmkB30hBr6VTo2tmmQTicp5AqoqkwioWAWTHRdRRSE0IM2o4aaPA9S1Qms\nvIUoBKRrkhjFLgTRJ12VwSjmkETCxBC7SELXaZg4ge+UfPiCIGD//v10dXWxcOHCipf2Pc/jxRdf\nJJ1Ov6z1H8kR2trayGazw9ooR9uTsqOjg23btlFTU0Mul0PX9fhEWQnSf/jwYVpaWjjttNO45ppr\n+OpXv8pb3vKWClz5oFFZL5XKYtTXq+u6vPDCC5x55pkvu9m7rktj43n4vk+mKolRKIY2WIUiggCq\nLOL7Psm0glN0SFZpOIXQg1YWwgpNqjqBnbfQqjXMrBmm+xA+pmdk3KJLulbHyhoIEkilxxRNADsg\nWaPj5AxESUAMStPTeqiL01MajllETyax3QKyJOMHYRUnmUpjO0X0ZALbzCGrCvc+uGVQn8mRI0do\nbm6mqanppNZ/eY56e3t7LEfor9XfE5GmXRAEzjjjjIpb+di2zYYNG0gmk7Hf90iTyMpRTmT/7d/+\njUQiwX/+53++kpGY/9Dr1fd9Dhw4gGVZvUpH3nj+tfx9zwEAdF3Bd0K2qKfk2MNZTyn4pU4KckCi\n9JFKqohUGhwLhICEGHrOBgSoSoAYhP/GgewhuCHhUpM+lAIOEpkAiuHf0mtEvFLSV7JGwcuVrqMm\ngZ8r6eXTKpQidiVVQvYi/1uBZL0GOKiaju9ZEIRcwQ8sJFVGwMWzLQQCpKSKb+cQFQlJkSFwEMSQ\nzL//c3+gKxsSx+EWj8p92is1SBYh8vXu6uoikUhUdIgMwu9L1LHdtm0bd999N2vXrh0VrtAHBr1e\nT7nKbLlvZU/84pdPkM91IYpQVZWmuzuHllBQVImu9ixJXUUQIbBtPAck0cUxbBQpAb6FVTCpqk3j\nFA2MboN0dRIjV6CYM0imw+qsZfvoKR2jkMPzbBKagmUWIFAQJRHL6KK+YVFcKd2zZw+iKFYstaMc\n0ZT4hAkTeh0k602O0NbWdpJur7+NMtKwjoYnJYREdteuXZxzzjlxpSoaInvppZeGPUQWIcq0nzhx\nItdccw133XUXb37zmyv+PsbQP/rqpvz04TVxO9MpDX8oioQFpYCScHOMct49L5IYyGC6JS/ZcJgr\nuiekMhp2rhS6UHROSvZKZnTsnFG6B4TE1bZMBEBJqvgFC0ES8H0XQZJxbAPwESQQ3AAlIWEZPpIs\nh17SgYXnBCAInDbljAE/h2gAMZ/P91oh6ZmjHskRolZ/RBojQ/Ke6E/TXglEdnkzZ86Mqy6VGCKL\nEEmN5s+fzx133EEqleKb3/zmKZPt/o+C/rqf+/ae0DUqEkRGBrZlxU4GkkjsUKCoIpQCTuQEBKV4\n2WSVip8vuRWkZITIj1YVwC5F1koQWHF9F9/0Y7cCx7ARS/TENosnORdIpf/vYSKVqqii6kNpiEzU\nBQr5VhAcRLEO08oiCYCq4QcOruOgpdK4vgGCC6YMgkfg+YiqjB+E956qumlkqmrIVNUMOMvS11o4\nevQoR48eHRWf9ojIOo7D0qVLY6eKKAxhJENkcCKMqb6+nhdffJF77rnnlSayQ8IpR2b7w+7d+0im\ndIxCEXwPXU9gmxaKKiIrMrZpUzUujWvZWEWLZEan6BjYhk0qo2PmDKyiiZZK4BQtPNdFURUC10UI\nfERRwDbzpJK1ocWXUSBTncGxwbKKJFMaliVw7Xs/Gg8vKIrC5MmT43Z6pRBVTGfMmDGocn5vG2W5\nJq7nRhlF646WhjVaTIsXL45Po5UYIotw6NAh2tramDBhAtdccw133303V1wxuDbwGCqLvjbHn//8\nSQB0PYFjh9UUoxDudglVwbBdlIQUD2pFGZlJXaVouqRrkti5cIdySm1JRZawgXRNOIAiiOBFaUFi\nuC0mq5LY3cU49QvAL7VIJU0m8E00PYFlFpBkEcfKEwQOgQ1iQCiJMLJIqlKa2Ialb1rZ72cQxUkL\nghC7gAyE3oY+m5ub2bFjx8vWwlA17UNFX3Z5Ix0ii1BOZL/whS+QyWT4xje+MUZkX2H0FRkPsHbt\nb5CV8LuQUFUEKSCVUfG8MALWdV2CAFxPQNZkJFFAUcALXHzPx7JMFBQEBEThhCWXqkk4JTKbSCqY\npYOtXlVu1SVDiQjLCRExcigQQXRO0BShTGIQyQoAPMdCjOy5/AKi4CNKEpaVg8AnEAU830LAQUTG\ndUwIrLACqyTwfK8kN/DwfQORgGlzT3YxGGrxKNK0j0brv7xDM2/evPh+U4khMjhBZMePH8+WLVu4\n9957efLJJ4ftZ/9K4JQjs319wH/96wZc1yWVSuAoEsWiQSaTxDYtHNMmmdYxcy5GvkgiqeAaTugl\nq4X2XAQ+kizgOR7JVAJXDE9/mXFpil15HMMmXZ0G0SbwPTJVKQI8At8lU12N55lIskTDuEamzZgT\nDy/U1NS8rLoyUg1rdOM/88wzXzbxOFhomsbkyZPjyNeOjo54o1RVlWKxOGoa1paWFvbv38/ixYv7\ndVwY6hBZhDEie+qgv81x/75DyLIU+jwKoc+sV2pbmoYJgCyLYVBCUsWzwo2t2F0s/e1wO1R0CccI\nU7/y2RwCIkEQPldLKbgFBwgwuvMIiPhe+LcTaRWvYIc+sVZoERQINqFvgo8kgSKJCJKP5Av4+Kgp\nBUHw0JJJZEXB8xwSusYbL722z8/A8zy2bt1KdXV1r3HSg0F/G6UoipimyZQpU0aFyJqmyaZNmwZ0\nXBjqEFmEnkS2qqqKe++9d4zIvkroa70++OOfYRTC4XEbA7/kpJVMabhmab0mZNxCPsxHkCBR+qoL\nEig++AEEBBhZAaGUPGvkRXxKEh9LxhHDOGrP13ElAwQBXUljK6E7QjIThp6AQKYqg10Ihzt1XSOw\nXTzRRdOTeLaDJ7qoiorgi/iCjyiICGG6CpqWwrXyCIKAltCxnSySKCJqMp6RDQe/RAXPswjEAEQf\nz84jiAKCAEuXf7jPz3Cg4pEoikiSNCrDWEEQsG3bNlRVZfbs2f3eb4Y6RAYnhsEbGhrYvHkz3/jG\nN1i7du0pTWThFCSzfeG/f/gghpFFCFS0hEIxX8S1AxI6uJYJCCRSAqaRR9WSBKKFZQbU1FaTc4oU\nDZvq6gy5bBbT9ElV6xRy3diWiJ5RMYp5XF8C36FgFKmqqcKyDFzXoKq6Css2CAyH6bNmsmnTppOI\nZjlpLPeerK6ujqsrg/1CR635oUTnDYRyq6CWlhb27NlDQ0MDO3bsiA2SI9I4UkTxt0P1uOtviMww\nDGpraxk/fjy5XI6urq6YyH7ta1/j8st79/sdwyuD3jbHbdt20ty8DwBV0fEcA1lPYVoFREki8ECU\nZGQJfNnCE2yQfWRVxrFDGYFl2TiiQ0qvxrALZNJprGIRQRYxTYtACpAUBUuyyGTSmMUCkhh2aBBF\nAkR8wSOhqwRmGH7g2gUEwcW1C3iug6qKOFYBVdOwnW48T8SVRRAENFHHtrJkaicjy70fyoYTJz0Q\nytdCY2MjGzdupK6uLj6QDiRHGAoiqVFvVkEDob8hslQqFevid+7cyfz58/n85z9PTU0NX//618eI\n7KuIvsjs/3thU/xzUtewSwdO17VjKUAqlcDIlqaxJB/8kt41mcAvhSioSQWxJB2SFBHB8kPxgBDg\n5mxEBCDA6c7HbgVW0IXohpIDU+hAtMP/b0sdCEbYXQlkJw5XcGQbqVTtDRIqgmmHguOkQhBYCISh\nC0HgIgci4CIi4AseguASSGEnRxRCHa3gOyhaJoy/FkDP1JNMD81ac/LkyTQ2NrJt2zZs2yaRSLB+\n/XoymQz19fXU1dWNWGoQSY2iGZqhYKAhskg+tGfPHiZOnMimTZteM0QWTmEy29PG5q9//RtaZMmV\n0pEVGaNokKlK4vo+Rs4gmdEQfQGnaKInNYycgVnIo6kyrmnjmAaKKOIZFr4qIfo+Vj6PWpNGdD3y\nuQ5qa2uwhYBioZt0JkUhV8S2TDRNxTBM5sw7r8+JxJ7VlWw2Gxsga5o2YHrW8ePH4wjfSgvFISSa\nhw8fZunSpfGi6nmiLG9DDHXDOXz4MMePH69IW6W8suz7Pp2dnezdu5euri7uu+8+Dh48yJ133jlG\nZE8B9LY5fv8HD5YeEzGLJamAbUEAqZSGmSsgClDMhhugJMr4VskntuAjqSJ+wUQCCp1diI6PmHIR\nbCd8juEgAEZXO4IHgSciWGbJgzL8m3Z3ATEQIGETuA5KWsdyXZKZJI5ZRJJC255A8JEkP9TZyyqB\nbyPIHrZpghDQtLT3gcJKxEn3h3JPyqga2rPLMpKNsrxiOtIUwfIDcxAEFAoFDh8+zPbt23niiSfY\nuXMnU6dO5Z577hkjsq8yeluvBw8dIZfLh6RVALcsUlYWRLySaCAiuAApTcMthuVb2zGRS5RXT6pY\nJTKbqtKxuwoAaNVJzK6w66JXa3j58G+puopQkh4IknDCoUAI3QriCRrnxDUHrg0lfa3nGrHW1sMC\n30WQpVA+JAa4gotkFAlkD8n38NywW+MJHrLogOcgCAKuXQxVCwJMn/ty682BUK5pj1r/5V2WgwcP\nDmvoM0LUAaqtrR1ximDkklRVVcXMmTOxbZuWlhY2bdrEtm3beOaZZ9i/fz/PPvvsa4LIwilKZnv6\nVnZ1dXP8eCu1tVVkO2yKhTyqKmMZLp7roaU07KKJ77qomoJvOciyhKzKuJZLur6Kgu1gF22q6zLk\nO7pxTIt0dYZiVw7PdkhWpfDF0PMyU1WFZefxPQc9ncSySjnOaoL3vP+jg9o0yvPJ58yZQ6FQiL0n\no5jJ8ePHk0qlEASBgwcP0traypIlSyruiABw8OBB2traXuZJ2Z8cYSgb5cGDB2lvbx+VtoooiuTz\neVRVZe7cuRQKBZYvX87999/PBRdc0Gce9BhGH33JDH772z8BoMgynmMjikIcYRvpV8PKDkiyiB8N\ngZU2tWRKx8wWEBQRv2T9Yxklz8qEgmU6PVK/Qms9SRHxDFCTKkFJtuCapWqQ5CF4HglVwbU9VE3H\ncU3EwMU2fBBCvaxRzJJIZHDsAqIoUDXhHHbs2MH48eOpra1FFMVYcz5v3rxRCQDpS9PekzRGOe/R\nRjnYLkv09xcuXEg6na7otUebeGdnJ294wxv4xS9+QW1tLZ7n8dBDD3HjjTdW9PXGMHj0tV5/8IMf\nx9VXRZHwS48ntBPSoEAIcO3Q8xkBPPME4U0ICl4oPogHOwHwyn4OylK/ZIj+S9VEnJKkIZGSCUr6\neDUVHlqBMNK6FJAiyAKyV9pjBFB9hUihm9A0wEdWJETRR1Y0RCFAED1cy0AUJCRRQhJ9JEVElAQU\nNU34llwgtP87Z/m/DOlz7UvTPthZloGKR+Wt/75SRUcCSZI4fvw4c+bMoaOjg+7ubi699FLuuOMO\nHnjggYq/3mjglCOzvflWPvDgQyR1hcCzSFclCAK/ZLklIUsivh+mell2MUz6cork8t0kkzpG3qJY\nyKGlE5j5IpZZRE3KuKaD61iIsoBj2qi6hGMbWMUuMjXVeI5F0cmTrqqCwMMyC8yZu2DYbYLywSfb\ntuMBqWIxTBuSZZmmpqaKE9lowrpQKLBo0aJ+F8xwN8p9+/bR3d09Ko4OAPv37yebzVJXV8d1113H\nvffeyyWXDP3kPIbRQc/N0TRNjh07DkAioVB07NADumgiCGCX/CMTagLbMVF1FbdgIkongg4ouSCk\nqlLk23MoCZmgtElG6V96SsewcyjaiWQvu1QFjiLkleQJ+x7TzCFJffTpAAAgAElEQVSKAo5tEAQ+\nihJ2bGRNw/NtwMEyu8LXKMVtZqrruWj5FXR2dsbBJYqixB6vI61o9obBelKWV1d62yj76rJks1m2\nb98+KnHYEJrDb9u2jQULFvDpT3+a0047jQcffHCsInuKoDcy+/Svf3/icdEn8KPnhjHTAMl0Aq9U\niQ0kj8Av2WvpMl5pbcqqhFMInyNKYOet+O9GQ5xAbPMFQFm0rSpLWJRcEBIKlhH50SawS/IGPaPj\n5kqDpGmNoBhWeCVNQZEFgoiYuh62VUBKSIhigKQqSLKAa+RK7gUCspZAFGVEUUDVq5kx/2IuvPrz\n6KnBu/sMxad9OMUj13XZtGkTjY2Nw/KBHwie57Fp0yYmTZrECy+8wHe/+13Wrl07KomFo4lTjswC\nsalzNDz06C/XUMx3YwhBGClbLODLMnpao6szT0JT0Eobml3Mo8gCrmkRqAKi4OAUbRTJh8DCzJtU\n1WRwPBMzb5KqSmHmCxRyJpnaKvLZIo5dQE8mKBYtbMdCURVcu8CyC4dmxN8XVFVl0qRJTJw4kZde\negnf91EUhRdeeKEipuoRoolHgIULFw5JXzeYjbK+vp729nYsy2LhwoWjRmS7u7upq6vj2muv5Zvf\n/CbLly+v+OuMYfiQJCn2JAX4yUO/IPDD5qBZqqb6pQqNqoVygjAgoaSxUyRcwpak2V1EFAXMfPh7\nUbszmUpQzLooqoRd0uZFHphaUsG0nZNIrRSAS1jFtU0bJZlAr0ojygGB6yLrKTzHJFlVjaLKuA4I\nYoLAd5EVGUmW0fQall36HkRRpK6ujrq6Oo4ePcqBAweYOHFirDmPDoAjMVWPUO5JOVQrnf42ynQ6\nzfjx45Ekib///e80NTVV5Hp7IiKy8+fP5/bbb+e0007jrrvuGiOypxBkWT6JzPq+z/Fjh0lo4X1f\nU0V8JcB2HEzTRJZk1ISKqkogJUCApC5jGxZBEJDQJZzAgwCSaQU75xP4Acmq0OMZIJFKYBVK6z0h\n45bIrCiBUzhBcp0SWYaTK79+echDcCKkQRYhekROiARB6CEtBA7gktBUHDOPkNTxfSu07JNlJFmF\nwMV3bfSaehZd9H7OvfwjQ/4sR+LTPpjiUU1NDTt37mTatGlMmDBhyNc3EFzXZfPmzUyaNIn169fz\nne985zVJZOEUJrPli23P7r1oegLTMBAISCZ17KKB6zooCQXXdpDTOpIk4hg2VXVVdFsOlmGRqU5T\n6MrhmBZ6SscuGBjFImoyzJD2XAdFUbC9Ir5joSU1bCsMW5AkEcfMkamuJvBlrnrHByr2Hl3XjT3c\norZE+UT//v37URQl/rIPdWOL9DvJZJJZs2aNeFCk50bZ3t5+UqukpaWlIgL3cuzbt49cLkdtbe0Y\nkT2FIcsyhUIh/u9f/Px/ISggyzK+54fTwYGKrAbIiggiJLQEIn6pveeQqkmgKAFyjYYsi+AH4T0g\n8EikZHxctLRKIhFWgQQBfNdDVmXwAwRRRElIOLaDkpBxS9XfoNSaVHWZwAuHRALPQVZEREkmwME2\nDYLAC1uOioKWSjF30YW87T1foKom3KCCIODAgQN0dnaydOnS+KAZecVu374dx3FiB47h+CYfPXqU\nI0eOVMSTsreN8sCBA7S2tpLJZGhpaanY0GeEqOK7YMECPvWpT9HY2MhXv/rVMSJ7iqGnl/svfr6K\nfC50MQi7I0H8s0KA74JpgWDJsaezkA5tMQESgo5bOrS6qo5rl362bILARBBEAtFDlB1EUUTRAddF\nEEQEEdTS5L8ohalhkqTgBz6C4KNVqaX5GQ81o4YSh8BDTiqln33EhISAgKQI4IUWW6IYEEgCjm2i\nJpO4VheCLCLJKoIYDnjq1VOYf+FNLFi6grq6uiF/jpGmvRI+7b0Vj44dO8aGDRtQFIVsNouqqsOa\nZekL5RXf559/nu9+97s8+eSTo+I5/0rglCOzPTU9v/71MwT4yIqCRiJM+9JVXFHAtRyqazN0d2Sx\nigbp6hT5rhyO6ZCpTlPszoPvk0yH5FdVZIKEgm87aGkV03bwTIdkTYq0oiNJAoIok0xqOK5BKpVE\nlFLIisKS8y5B0ypz47csi82bNzN16lQmTpx40nuP9DWzZ8/GMIyTAgbq6upoaGggnU4P6BE3mp6U\noijS1tZGXV0ds2fPJp/Pv+xEWV9fPyI3hnIie9111/Gtb32Liy++eETXfdNNN8UxfC+++OLLHg+C\ngFtuuYWnnnqKZDLJgw8+yJIlS0b0mq93hBuHGK9X3/fZtGkTnuehaQq26aAnNMxiHgAJFcey0dQq\ncl3dpDJJukuJYLYq4bkeNbUZCtkcqeo0hWw+3GCLhNXedKiPT9eEHRVJlihYYVXIt3V830TRqnDy\nBkoigevkkRICCA4ILkIghCEJYjg4EvgeCKCoCjXjJ7Ls0mu5/J8+dtKGEQQBu3btwnXdl0lpyr1i\nXdelvb099k0eiptJX5r2Sv0bmaaJYRi86U1vwvf9ig19RignsrfddhtTpkzhK1/5yog23rH1+srg\ngQd+Ev8slm0rmpbAK1lyJdMabmn4K6ErMZGVZQmzEJLXsNtyYkDMty18zwd8XCO00ARIKGDmQ5lA\ndU0SI1v6eVwaozu8T6RLEdcAqeoUZndpiCydxDPC5yuaihVLGiTUVBUiHp7jgiSUeIMAvoWs6vi+\nTeD7zFp0CZe/+06q6xvjIe29e/eSSCTiA+BAA9ij7dMOcOzYMZqamqiuru61yzKS4lFEZCdPnsxf\n/vIXvve977F27doRE9lXc82ecmQWTlRmu7q6+Ma936RY6MR3FXRdo5jLIQkJ9JRKMZfDtSCVEnGs\nIiISqbSC55kosk46o0Jgoyc1VEWHwCNTpeN7KrIkUF1Xhe95SCK4roXneMiKhB84qKqCnkyxZNnF\n3PihO6iuHfrJrTdEp7mBPB0htL6JPOIcx6G9vZ39+/eTz+djq6poICVCpN+ppFVQOXzf56WXXiKZ\nTMYpRL3JEXbt2tWv92R/iDS+NTU1XHvttXz729/moosuGvG133jjjXz84x/nfe97X6+P/+pXv2L3\n7t3s3r2b559/no985CM8//zzI37d1zuigU3Hcfj5z3+BXRq8EksHLkkUcaEUHxtudtFUtKKEIQjJ\ndBK7GG5SkftB9PvpTAojl0cUBdzS3xZKlSVVU3GLBqIk4hhmWHHyPDzXIZnRMbvzSLKMiIYYgO+7\nSLKAVehGlEUUVWXS9DO46vrbWHDOy3XYnufF3/fTTz+930OkLMuxb3IUMBC5mei6Hk8xl3svB0EQ\na+cH0rQPF5GLSbld3kByhKFslBGRnT9/PrfeeivTpk3jy1/+8ojfy9h6HR1E3+EgCNi3bx+bN5+I\naU7qWjxomVBlSnJUtIRMSflDMqlRzIYSgFRax8yVCGgmhZUPSaee0vCtkjetKsdEVhCJyS+AbZyQ\nJ0VrGyAokxUIZSm/siQQiQ8UTcHNh7+jpjQIHERRIBB8XMdBEFyQFJAlktV1LHjj27nonf96ks1e\nzyHttra2Poe0IwxW0z5cRC4p5XZ5lRj6jFBOZP/85z/zgx/8oCJEFl7dNXvKktnW1lY6Ozv5+9/3\nousatmWSSmooangqzKR1LEmkmC9QO64Gs5An391NuipDrrsAvoemq+Sz3QiBiyAIFPJ5qmur8GyH\ngmVQXVuDmc+Tqc2QUCTCfdChpn48TUsv4dKr3hNq4VKVOXl1dXXF1YuhnuYURWHixIlMnDgxjqyL\nBlJSqRTjx48nk8nw0ksvDUu/Mxj4vn+SOXxv6E23d+zYMXbu3DmojTLa2Kurq7nuuusqRmQBLrzw\nQvbv39/n42vWrOF973sfgiBw3nnn0dXVRXNz86iI7l9PkGUZ0zR54YUXWLfuWSB0MYj0sqIQbkZ6\nQiVnmmFyX0lj65Y2OVWVsYuQKiO1RmnTk6WQFKXSKcx8vjREZpX+drjJJPQErmEgiEK8oUaDJVpK\nR8AncC2QBBzTREulmbvkTVzzwTupm9D7dHAUJ93Q0MCUKVOG9JmUBwxEMc4tLS1s3rwZICa2hw6F\n8aFD1bQPFkeOHOHYsWMsXry4Vw3+YHR7/XVZurq62LFjBwsWLOCTn/wkM2fO5Mtf/nJF3svYeh09\nBEHA1q1baW5uxjDCyGdBANs6UVktt+Fy+tCvCmX61XLSqSgikYxeTSRwSwNiup7AjeQJuopfci9R\nNSUms5Ik4pRYtCAQV4SB+PkAgn/itVVVgcDGcRxkWUTEhcCnumEqF/7TJznrgncO+JlEQ9rTpk07\naUi73OfcdV327ds3LE37YFAoFNiyZUufdnnDHfqMEBW7pk6dynPPPRcT2eGGM/XEq7lmTzkyG1m6\nuK7L1KnTaGtro7a2GtsyMYtFqqoyZLu6MIsmmUyGfDaLa9ukMmmMfB7f89CTOpZhkE5qyIpSIry1\n2KKAkctTVVOFYxlYxQJaOoUsBLiuzWmTp3Pz/3cH57zxktgrsbW1lU2bNo140CNqZSxevHjEi6A8\nsi4IAvL5PEePHmXbtm2kUimKxSLFYrGip8bynObBbuz9bZSiKMYberRRRjeOiMj+13/9FxdeeGHF\n3sNAOHLkyEnvbfLkyRw5cmRsc+wHgiDQ3d1NR0cHS5cuZf36/weE5uqFvIsiSzEpjfR2up4gZ1lo\nmopV2tiijUxVFexiyamg9HtRpVaSQoKUSqewiiFhjTZGVZbD6m9SxymGsgWrFJ0rSQKB5+D7DlVV\n41j25pt42/s/3e+A5VDjpAf6jFKpFDNmzGDGjBmxp+OGDRsIgoDTTjuNrq6uioQglGOodnlD7bKU\nE9lbbrmF2bNn86UvfWlUSHlvGFuvw4NlWRSLRSZPnsyDD/40tuTSNCUeqkwmE7glNqqqcrxOJUmI\n16MggFVGNL0yb1q8cpJ7ArIk4xL+LV1LYJTIqSzLeG400KnhlA60mn6iwqsm1Ni6T5IlnLLr8H0H\n37MRBRdZTjJ57vm8+f/cycTp84b1GUVD2pMmTcL3fTo6Oti7dy/ZbJbx48eTzWaRZbmi7kPDscsb\nzNBnVDwqJ7J/+tOf+J//+Z+KEtnBYDTX7ClHZtva2hAEgcbGRhRZ5ktf/iL79+5HTSgkFBnXdeju\n7sa2TAQC8rkcrmMRBH4oO/BcRFGimO9GJEBPJeONL1NdjW0YOI6DlkzimgaZhMqchYt4+w0f59xl\nb4xvxIIgkE6nSafTzJgx42WDHvX19YPSr0IYJnDs2LGKDHb0RPTaEZlIJBJxvK5t2yMaSIkQTTxO\nnDhx2J6ufW2Uu3fvxjRNRFGku7ub6dOnc9111/Gd73yHCy64YFivNVyUD0WUX/cY+obrujQ3N5PJ\nZEgmk/zbv32WLVs2h/7PiVAfWzTyFPIFRBFyXVkSCZXubBeKImHk8yHJ9V0sy0JLyIiEm5iAgCSJ\neLaFJEllLclwo0ymdJxS9deKdHSShAMk02nsYljFLRa6OW3yVC6/9qNc+rYbBiR2+XyeF198cVip\nWIOBJEm0tLQwffp0GhsbT9qAqqqqYp3tSDbKStjl9ddlUVWVzs5OZs+ezS233MKcOXO48847X9H1\nMrZeh4e9e/eiaRoTJ07kXe96O/X11TQ3H6N+3DhMo0ChUITAxyjkKRYLCIBjmeRz3UiSCL5HIZ9H\nkSUCz8E2LURJQPC98LsmgOeGP/uBT7GkjwVwylxPIskRgEQQ+876Zd60siQS1WJVVcZxSzHWSQ2n\ntK+rSR3HyKElEyy84J2suPmrFeumQlg8iiw0L7roIorFYkWGtMuRzWbZtm3biJI/+yseRbr5ZDLJ\nH//4R370ox+xdu3aUfHI7g+juWZPOTI7btw4pk6dyq5du8hkMixatIgbbrhhVDYV27bZsmULEyZM\nGLDaWD7oEelX9+3bR6FQiEv7PXWhkcdrPp+vSCpWb4j0O+WLoKfrwOHDh+nu7h5WvG50mpsyZcpJ\nw2ojRXn83+7du8nlcvzxj3/kpptuYvHixfhlLaRXCpMnT47bvhAeQkZDd/x6giAILFy4kPXr1/Pi\niy8ydeoUzj33DUybNq3ixCIaxNqzezfTpkzGtorYpkG+O4tlFrDNMPHPLOawLBMJD9d2uPqGfyFV\nO4GWlhbWr18fy3J6ZpLDifW0YMGCiocJQO+elOUbUORmsm/fPlRVja9zsBtlpME1TbOidnnlG2VH\nRwfbt2/HNE2uuuoqgiBg2bJlrziRHFuvw8OcOXPi76GqKlx22WWj4nEO4Xp67rnnOGPOLDzHxijk\nMIoF8t1ZXMfEyOcxjQK2WaCY64bAwzaLmIU83dlOAs/DLnYjiQJJXcMxQwmhIIokNB3PsXGCgIuv\nu5Ur3/evFb/+3jTt5UUZwzBoa2tj27ZtuK4bF48ymcyg10N0z1m0aFHF7PLKi0dTp05lw4YN1NXV\n8YlPfIIdO3Zwww03xF3QVxKjuWZPOTL7ta99jaeffpqZM2cyc+ZMLr/8crZv3x4Txtra2orcNKNM\n8lmzZg1ZX9pTv1qeSZ7JZGhoaKC2tpbdu3cjiiJnnXXWqNzoB/KkHEy87vjx408aSCmHbdts2rSJ\n6dOnj7jV2huiG0VkafTMM8/w2GOPkU6ne80OH22sXLmS++67j+uuu47nn3+e6urqsZblAPjb3/7G\nzTffzNlnn41lWXz84x/n2LFjZLNZGhoaeiWMw4Hv+2zbtg1FUVhx5ZXDWk+RfjVy39i4cWO8RqLW\n4YEDB0ZNDzeQJ2VPN5OoAhS5mUSDHn11gyKy73ke8+fPH5V7TmdnJ7t27aKpqYlPfOITvO997+ND\nH/oQGzZsqPhrDYSx9Tp0BEHA5ZdfTkNDA7Ztc/PNNzNu3Dg2btzI+PHjaWhoqJg8rbm5mUOHDnHZ\nZZeNaD3Ztk1raystLS1YlhWvA0VRhr2HDwaD8WnXdf1lRa4DBw6Qz+epqamJi0d9HSrb29vZs2fP\nqN1zHMdh48aNzJw5k9/97neoqsr27dv529/+1ue+P5oYzTUr9Fb27QdDevJwEAQBt912G7///e+p\nr6+ns7OTK6+8Ml4Q2WyWqqoqGhoaqKurG1blIRrEqkQmec9r7+7u5vjx4xw+fBhN05g2bVq/hHG4\niCaUFy1aNCyyEOmB29raep3cjDbe2bNnD8uDbyAEQcCePXtwHIdkMsm73/1uvv/97/PGN76x4q8V\n4d3vfje///3vaWtrY8KECdx55504JY3Yhz/8YYIg4OMf/zjr1q0jmUzywAMPcM455wz0Z0/lvuao\nr1eA3/zmN3zwgx/k7LPPZvv27SxbtoyrrrqK6dOn09nZiSzLMWEcyPKmN0SDWOPHj6+41Vxkf3fo\n0CEsy4rt8npOMI8U0eF5uJ6UjuPQ1tZGa2srhUKB2tpaGhoa4m5QEARs374dSZIGdF0YLjo6Oti1\naxcLFizgYx/7GAsWLOCLX/ziqFVkx9br6KCrq4uLL76YqVOncuTIESZOnMjb3vY2li5dSrFYxLbt\nIcnoeiJySchmsyxcuLCiFd/I/u7o0aN0dHRQX1/P5MmTX+bqM1JEPu2pVCp27Rnq70dD2h0dHb12\ng6IOzKJFi0aFWEZEdsaMGfz2t7/lwQcf5PHHHx/VauworNlBf/CnHJkF2LZtG3PnzkUURdrb23ni\niSd47LHHOHDgAJdccgkrVqxg3Lhx8ZckqgANZtEcP36c/fv309TUNConIdu22bx5M42NjVRXV9Pa\n2kprayuCIMSV0JGefCNPyrPOOqsiN4pocrO1tRXDMKiqqqKjo4N58+aNShJIEATs3r0b13XRdZ3r\nr7+eH/zgByxbtqzir/UK4B9+czx+/Hg80Oc4Dn/4wx949NFH+dOf/sRZZ53FypUrOfPMM+nqCqNi\no27BYFpqhmGwZcuWigxi9YboUGWaJqeffjodHR20tLRQLBbjluFIB7Mq7Unp+34cr9vZ2UkqlcKy\nLKqrq5kzZ86oEdndu3czf/58PvrRj3LWWWdxxx13vBY1qqfyBb8i6zUIgjilLToErVq1iieffBJd\n17nqqqu48MILcV2XQqEQ+5tXV1cP+O/t+/5Jh6rRsJqLDlXz58/HcRxaWlro7OyMB54GywX6QqV9\n2suHyaOZIE3TyOfznH322RWfo4ETXdUZM2bw7LPP8pOf/IS1a9eOmifuKOK1TWb7Qj6f51e/+hWP\nPfYYW7Zs4fzzz+fqq6+msbGRzs5OVFWNK0A9TzpRgk9HR0fFSGBPRP5ws2fPpr6+/qTHLMuKiW00\nmNXQ0DAkbU2kwS0UCixYsGBUbhS5XI5NmzaRyWQwDKOi8bpwgsiGpvoa119/Pf/93//NeeedV4Gr\nf1XwD7859gXP83j++edZtWoVzz77LNOmTWPlypWcffbZ5PN5XNeNK0C9VUKjoYhKd1AiRNIFVVVf\nRgKjgafW1taTukFD0ZvDCT3cwoULRxQi0hc8z2Pjxo0IgoDneRWP14WXE9mmpia+8IUvvBaJLIyt\n175fPAg4ePAgq1ev5rHHHsOyLK688kouvfRSZFmO5y4mTJjQayU06qBEJHA0vh+RdKGpqemkLk/5\nwFNbWxuKogyrG9Sbpr3SOHDgAIcPH0bX9ZOGtAdzWBgMIiI7c+ZMnn76aR566KHXKpGF1yuZLYdl\nWfz2t79l9erV/PnPf2bJkiWsXLmS008/nc7OzrhS1NDQgKqqsfYlqvhWGt3d3bz00kuD2nijVklL\nS0u/AQjlCIKAnTt34vs+Z5555qjcKPL5PFu3bo0rSOUDKe3t7SOe3Iw0fUEQoKoq73nPe/jhD3/I\nueeeW5HrX7duHbfccgue53HzzTfzmc985qTHDx48yPvf/366urrwPI+7776bFStWjPRlxzbHQSBq\n261atYqnnnqK6upqVq5cyfnnn49lWRiGER/wqqqqaGlpYf/+/Zx11lkVI2XliOKk6+rqmDZtWr/P\njfTmLS0tdHR0xAEIkW6vL0Sa9tHqAkV2eXV1dXEFKXJdaW1tHXG8LpzQ9C1YsIAPf/jDLFmyhM99\n7nMVuf+MrdeX4ZRZr0EQ0Nraypo1a1i9ejUtLS1cdtllvPWtbyWdTtPV1RUXOurr6+Nh6tHqoADs\n379/0MUowzBoaWmhtbU1ltFFh+a+MJCmvRI4dOhQ3FWNwqHa29tpbW2lu7s7djOpq6sb1sC4bdts\n3LiR2bNns27dOn7605+ydu3aigyznurr9TVLZsvheR7PPfccq1at4re//S1z5szhbW97G01NTXR0\ndMRpUqeffvqoTCi3t7eze/duzjrrrCFLCHq2DHurhEapW7quM2vWrFEhshEZ788aJNIXtra2Dile\nF04msoqi8J73vIf777+fN7zhDRW5fs/zOP3003nmmWeYPHkyS5cu5Wc/+xnz5p3wGfzQhz7E4sWL\n+chHPsK2bdtYsWJFvwbPg8TY5jhERIN/q1ev5vHHHwfgyiuvZPny5UBIAmVZZu7cudTX11f88NlX\nnPRgrz1qGba2tvZZCR2ppn0gDMYuLzo0t7a2xvG60XDqYDbKiMjOnz+fj3zkI5x99tl89rOfrcj9\nZ2y99opTcr1C2CV58sknWb16NTt37uTiiy/myiuvZMKECRw7dgzXdZk2bRpTp06t+Pc92jscx2He\nvHlDvh9EMrqWlhZM0+y1EjpSTftgsH///lhH3Nt7KB/Sbm9vj4e06+vrB1Vd7klkH374YZ544omK\ncJ7Xwno95dwMhgNJkrjwwgu58MIL42z4Rx99lHvvvZeOjg5uuukm/umf/inWxkWntKG0+PtCtGkt\nWbJkWCJuURSpq6ujrq7upEro/v37UVWV+vp6WlpaqK+vH7CCNFxE5ucDxfMNJ14XTlSVBUEYFSIL\nsH79embPns3MmTMBuO6661izZs1Jiy0y+Ifw5jxm4/PqQBAEZs+ezb/+67/yqU99imPHjrF69Wo+\n//nPs3fvXubPn8/tt98eJ/BEDiHDrVaUYyhx0n1d+0D+057nkcvlWLJkyajY8Q22FdpXvO6ePXv6\njNeNEH328+fP58Mf/jBLly7lM5/5TMUO0mPr9bWF6upqrr/+eq6//noMw+CZZ57h4Ycf5g9/+AOi\nKHLPPffg+/5JDiENDQ3DGvgsR9TRGUycdF8oD0CIKqFHjhxh+/btVFdXk06nOXz4MAsXLhyVVnwk\nDywWi/3a5QmC8LJ43dbW1gHjdeFkIvurX/2KRx55pGJEFl4b6/V1UZntDb7vc8UVV3DTTTexf/9+\n1q5di6qqXH311Vx00UV4nheTsKhaMZSFEmlwOzs745ZBpZHNZtmyZQuiKMaek9GXuVKIxPQjsQbp\nbSClXIgfEVlJkrjhhhv40Y9+xNKlSyv2HgB++ctfsm7dOu6//34AHnroIZ5//nnuu++++DnNzc1c\nccUVdHZ2UigUePbZZzn77LNH+tJjlZ4K4ctf/jK2bTNr1izWrFnDvn374oHP+vr6uMU/XMuvSINb\nqUGsnnAch23btpHNZlEUpU//6ZGgEnZ5vQ2klG+UbW1t7N27l3nz5vEv//IvnHvuuXz605+uaEdo\nbL32itfUet26dSu33norH/vYx3jmmWf44x//yIIFC1i5ciXz58+nq6uLIAiGbfk1kjjpwcD3fQ4f\nPszevXtRFOUk2USlqsvlw84jkQf2HNKOikc1NTXx4XbOnDk8+eST/O///i9PPPFERXnCa2G9vi4q\ns71BFEV+/etfxyTzs5/9LIcPH2bVqlXcfvvtFItFVqxYwWWXXRan2gzW8iuqNHqeN6KEnf5gWRY7\nduzgjDPOiD0BW1tb42jJaPMZSbJXVH1ZvHjxiE7QPavL5T6epmnS3t5OOp3mjjvuGBUiC4NLFvnZ\nz37GjTfeyG233cZf/vIX3vve9/Liiy+Oyr/fGIaOz33uc/F6vfHGG8nn86xbt44HH3yQTZs2cf75\n57Ny5UqSySQHDx4c0pBHuSfzaGhwI+mEqqpccMEFBEFwUsAHe74AACAASURBVGJWJarLlmWxadMm\nZs2a9bIB06GgZ3U5urfs3r2bfD5PsVjENE2+9a1v8cY3vpHbb799VAIweruucoyt11MbCxcuZN26\ndUiSxNvf/nZ832f9+vWsWrWKr33ta0ybNo2rr76ampqaOJFysJZfkX51+vTpTJgwYVSuv729nebm\nZs477zwSiUSf/tPDLfJEPrWiKI54zqVndbmzs5Pjx4+zY8cObNumvb2dzZs388QTT1ScyMJrY72+\nbskscNKmIQgCU6ZM4ZZbbuGWW26Jxe133XUXR48e5bLLLmPFihV0dHSwZ8+ePi2/PM/jxRdfJJ1O\nc8YZZ4yKfjXS75S3QlVVpbGxkcbGxlgLd+jQIXK5HDU1NXF1ebBfnOPHj3PgwAEWL15cUY87QRDI\nZDKk02lM0ySdTrNz504+97nPkUql2LBhw6iQ2cEki/zoRz9i3bp1ACxbtiyO1B2tgYUxDA09SV46\nneZd73oX73rXu7BtOx74fO6551i8eDErV66kqqqKrVu3AsQVoJ5k9ciRIzQ3N49KnDT0rmmPqp31\n9fWxfKilpSWOEo3uLYNde5FTynDlEf0hureoqsrevXtpaGjgk5/8JIcOHWLcuHG4rlvxz21svb4+\nUL5mRVHkvPPO47zzzuPuu+/mpZdeYtWqVfzwhz+kurqaq6++mje96U1xcmZfll/RIPLcuXNHTb9a\nLg+MvtuZTIZMJsPMmTPj+ZDywJK+XFd6Q+SUomlaxedcJEmivr6eTCbDhg0bmDVrFj/5yU949tln\nWbx4Mbt372bRokUVez14bazX163MYCjo7u7mqaeeYtWqVWzfvp2LLrqIq666itNOO42Ojo7Y8qum\npoYdO3YwYcIEJk+ePCrXEnlSDtaOKDJnjrz2BuO729zczJEjR2hqahqVzT3yLlQUBd/3ef/7388D\nDzzAGWecwZEjRzjjjDMq/pqu63L66afzm9/8hsbGRpYuXcrDDz/M/Pnz4+e89a1v5dprr+XGG29k\n+/btXHrppRw5cmSkN5qxtuUrDM/z+POf/8yqVav4zW9+w5w5c1i5ciWLFy+mu7sbx3FiSc7x48dj\nK7vRkAJFjgLjxo0btKa9UCjQ0tISt/gH8p+ODrdnnnnmqBmeRwbu8+bN4+abb+aCCy7g1ltvZcuW\n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\nsvtP5LWUUsqOjg758Y9/XJ533nnyvffeS9n1U0iyGhrLjxGj9JpaKioq5I9//GNpGIZ8++235Te+\n8Q25Zs0aWVhYKH/1q1/JAwcOyLffflu++uqr8oMPPpB1dXWyq6srKc0ePnxYvvnmm7KhoUF+4hOf\nkA8++KA0TTNlv8NZotkzrclR0auUp69ZpdcPMU1T3n333bK9vV0eP35c/td//ZfcsmWLPP/88+W9\n994r33nnHbl37175yiuvyN27d8tjx47Jjo6OpPTa2NgoX3nlFdnY2Cjvu+8+efXVV8tQKJSy32Gy\n6XVMxXa6vP322/KSSy5xPv/BD34gf/CDHwx6/L59++RHP/rRsbi1cUEoFJI7d+6Ut912m1yxYoX8\n/Oc/L5944gl56NAh+cYbb8jXX39dFhUVycbGxmGN7F//+lfHyK5cuVKWlpam9F4TfS2/+tWvyj/+\n8Y/yb/7mb8aj0KQ88wOg0usExTAMefDgQfnd735Xbtq0SV588cXyf//3f+X+/fvlu+++K1955RX5\n/vvvy+rqatnZ2ZmQkT1x4oT8xCc+IX/5y1+m1MhKedZo9kxrUul1gmKapmxsbJQPPvigvPzyy+X6\n9evlt771Lblr1y75wQcfyFdffVW+9dZbsqysTLa1tSVsZL///e/LT33qUyk1slJOPr2OSZpBqhio\nYrOurm7Q43/9619z+eWXj8WtjQs8Hg+XXXYZDz74IAcOHODWW2/lrbfe4tprr+W///u/qaqqwjRN\nKioq2L17N2VlZbS3t1uzmhiGYXDgwAHy8/M5efIkX/ziF3nyySdZunRpSu81kddy//791NTUcOWV\nV6b02oqxQel1aDRNY9WqVdxzzz28++67/PznPycYDPIP//APfP3rX2fPnj0YhkFTUxPvvvsuRUVF\nNDU1YRhGr/NUV1dz8uRJFixYwDXXXMN1113HrbfemvIcWaXZsxul16GxNza67bbbeOGFF3jttddY\nvXo1DzzwAJ///Od55pln6OzsJBAIsH//fvbu3UtNTQ3BYLDXeTo7Ozl06BCrVq3il7/8Je+99x6P\nP/54SncBhcmn1wm1nW286bIZ7IH9yCOP8P777/OXv/xltG9rXKLrOhdeeCEXXnghpmnywQcfsGPH\nDn7yk58wdepUrr76aj760Y/2avk1depUqqqqmD59Ok1NTXzpS19i+/btLFmyJOX3N9xraZomd955\n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\n", - "text/plain": [ - "<matplotlib.figure.Figure at 0x760af28a59b0>" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "with open('/tmp/colorsys_led_mapping_results.json') as f:\n", - " poly_data = json.load(f)\n", - "\n", - "#sample_entry = poly_data[sorted(poly_data.keys())[len(poly_data)//2]]\n", - "fig, axs = plt.subplots(3, 3, figsize=(12, 12), subplot_kw={'projection': '3d'})\n", - "for i, ax in enumerate(axs.flatten()):\n", - " entry = poly_data[sorted(poly_data.keys())[len(poly_data)*(3+i)//12]]\n", - " polys = Poly3DCollection(entry)\n", - " polys.set_facecolors([\n", - " tuple(max(0, min(1, e[0][i])) for i in range(3)) for e in entry\n", - " ])\n", - " ax.add_collection3d(polys)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/research/colorspace/cccie31_locus.csv b/research/colorspace/cccie31_locus.csv deleted file mode 100644 index 400d1d4..0000000 --- a/research/colorspace/cccie31_locus.csv +++ /dev/null @@ -1,95 +0,0 @@ -360, 0.175560, 0.005294, 0.819146
-365, 0.175161, 0.005256, 0.819582
-370, 0.174821, 0.005221, 0.819959
-375, 0.174510, 0.005182, 0.820309
-380, 0.174112, 0.004964, 0.820924
-385, 0.174008, 0.004981, 0.821012
-390, 0.173801, 0.004915, 0.821284
-395, 0.173560, 0.004923, 0.821517
-400, 0.173337, 0.004797, 0.821866
-405, 0.173021, 0.004775, 0.822204
-410, 0.172577, 0.004799, 0.822624
-415, 0.172087, 0.004833, 0.823081
-420, 0.171407, 0.005102, 0.823490
-425, 0.170301, 0.005789, 0.823911
-430, 0.168878, 0.006900, 0.824222
-435, 0.166895, 0.008556, 0.824549
-440, 0.164412, 0.010858, 0.824731
-445, 0.161105, 0.013793, 0.825102
-450, 0.156641, 0.017705, 0.825654
-455, 0.150985, 0.022740, 0.826274
-460, 0.143960, 0.029703, 0.826337
-465, 0.135503, 0.039879, 0.824618
-470, 0.124118, 0.057803, 0.818079
-475, 0.109594, 0.086843, 0.803563
-480, 0.091294, 0.132702, 0.776004
-485, 0.068706, 0.200723, 0.730571
-490, 0.045391, 0.294976, 0.659633
-495, 0.023460, 0.412703, 0.563837
-500, 0.008168, 0.538423, 0.453409
-505, 0.003859, 0.654823, 0.341318
-510, 0.013870, 0.750186, 0.235943
-515, 0.038852, 0.812016, 0.149132
-520, 0.074302, 0.833803, 0.091894
-525, 0.114161, 0.826207, 0.059632
-530, 0.154722, 0.805864, 0.039414
-535, 0.192876, 0.781629, 0.025495
-540, 0.229620, 0.754329, 0.016051
-545, 0.265775, 0.724324, 0.009901
-550, 0.301604, 0.692308, 0.006088
-555, 0.337363, 0.658848, 0.003788
-560, 0.373102, 0.624451, 0.002448
-565, 0.408736, 0.589607, 0.001657
-570, 0.444062, 0.554714, 0.001224
-575, 0.478775, 0.520202, 0.001023
-580, 0.512486, 0.486591, 0.000923
-585, 0.544787, 0.454434, 0.000779
-590, 0.575151, 0.424232, 0.000616
-595, 0.602933, 0.396497, 0.000571
-600, 0.627037, 0.372491, 0.000472
-605, 0.648233, 0.351395, 0.000372
-610, 0.665764, 0.334011, 0.000226
-615, 0.680079, 0.319747, 0.000174
-620, 0.691504, 0.308342, 0.000154
-625, 0.700606, 0.299301, 0.000093
-630, 0.707918, 0.292027, 0.000055
-635, 0.714032, 0.285929, 0.000040
-640, 0.719033, 0.280935, 0.000032
-645, 0.723032, 0.276948, 0.000020
-650, 0.725992, 0.274008, 0.000000
-655, 0.728272, 0.271728, 0.000000
-660, 0.729969, 0.270031, 0.000000
-665, 0.731089, 0.268911, 0.000000
-670, 0.731993, 0.268007, 0.000000
-675, 0.732719, 0.267281, 0.000000
-680, 0.733417, 0.266583, 0.000000
-685, 0.734047, 0.265953, 0.000000
-690, 0.734390, 0.265610, 0.000000
-695, 0.734592, 0.265408, 0.000000
-700, 0.734690, 0.265310, 0.000000
-705, 0.734690, 0.265310, 0.000000
-710, 0.734690, 0.265310, 0.000000
-715, 0.734548, 0.265452, 0.000000
-720, 0.734690, 0.265310, 0.000000
-725, 0.734690, 0.265310, 0.000000
-730, 0.734690, 0.265310, 0.000000
-735, 0.734690, 0.265310, 0.000000
-740, 0.734690, 0.265310, 0.000000
-745, 0.734690, 0.265310, 0.000000
-750, 0.734690, 0.265310, 0.000000
-755, 0.734690, 0.265310, 0.000000
-760, 0.734690, 0.265310, 0.000000
-765, 0.734690, 0.265310, 0.000000
-770, 0.734690, 0.265310, 0.000000
-775, 0.734690, 0.265310, 0.000000
-780, 0.734690, 0.265310, 0.000000
-785, 0.734690, 0.265310, 0.000000
-790, 0.734690, 0.265310, 0.000000
-795, 0.734690, 0.265310, 0.000000
-800, 0.734690, 0.265310, 0.000000
-805, 0.734690, 0.265310, 0.000000
-810, 0.734690, 0.265310, 0.000000
-815, 0.734690, 0.265310, 0.000000
-820, 0.734690, 0.265310, 0.000000
-825, 0.734690, 0.265310, 0.000000
-830, 0.734690, 0.265310, 0.000000
\ No newline at end of file diff --git a/research/colorspace/cccie31_locus.svg b/research/colorspace/cccie31_locus.svg deleted file mode 100644 index 02df99d..0000000 --- a/research/colorspace/cccie31_locus.svg +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - version="1.1" - width="200mm" - height="200mm" - viewBox="0 0 200 200" - id="svg2"> - <g id="layer1"> - <path id="path2991" - style="fill:none;stroke:#000000;stroke-width:0.1mm;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="M 0,0 L 35.112000 1.058800 35.032200 1.051200 34.964200 1.044200 34.902000 1.036400 34.822400 0.992800 34.801600 0.996200 34.760200 0.983000 34.712000 0.984600 34.667400 0.959400 34.604200 0.955000 34.515400 0.959800 34.417400 0.966600 34.281400 1.020400 34.060200 1.157800 33.775600 1.380000 33.379000 1.711200 32.882400 2.171600 32.221000 2.758600 31.328200 3.541000 30.197000 4.548000 28.792000 5.940600 27.100600 7.975800 24.823600 11.560600 21.918800 17.368600 18.258800 26.540400 13.741200 40.144600 9.078200 58.995200 4.692000 82.540600 1.633600 107.684600 0.771800 130.964600 2.774000 150.037200 7.770400 162.403200 14.860400 166.760600 22.832200 165.241400 30.944400 161.172800 38.575200 156.325800 45.924000 150.865800 53.155000 144.864800 60.320800 138.461600 67.472600 131.769600 74.620400 124.890200 81.747200 117.921400 88.812400 110.942800 95.755000 104.040400 102.497200 97.318200 108.957400 90.886800 115.030200 84.846400 120.586600 79.299400 125.407400 74.498200 129.646600 70.279000 133.152800 66.802200 136.015800 63.949400 138.300800 61.668400 140.121200 59.860200 141.583600 58.405400 142.806400 57.185800 143.806600 56.187000 144.606400 55.389600 145.198400 54.801600 145.654400 54.345600 145.993800 54.006200 146.217800 53.782200 146.398600 53.601400 146.543800 53.456200 146.683400 53.316600 146.809400 53.190600 146.878000 53.122000 146.918400 53.081600 146.938000 53.062000 146.938000 53.062000 146.938000 53.062000 146.909600 53.090400 146.938000 53.062000 146.938000 53.062000 146.938000 53.062000 146.938000 53.062000 146.938000 53.062000 146.938000 53.062000 146.938000 53.062000 146.938000 53.062000 146.938000 53.062000 146.938000 53.062000 146.938000 53.062000 146.938000 53.062000 146.938000 53.062000 146.938000 53.062000 146.938000 53.062000 146.938000 53.062000 146.938000 53.062000 146.938000 53.062000 146.938000 53.062000 146.938000 53.062000 146.938000 53.062000 146.938000 53.062000 146.938000 53.062000" /> - </g> -</svg> diff --git a/research/colorspace/cccie31_locus_edited.png b/research/colorspace/cccie31_locus_edited.png Binary files differdeleted file mode 100644 index 0ce10c8..0000000 --- a/research/colorspace/cccie31_locus_edited.png +++ /dev/null diff --git a/research/colorspace/cccie31_locus_edited.svg b/research/colorspace/cccie31_locus_edited.svg deleted file mode 100644 index a72e11f..0000000 --- a/research/colorspace/cccie31_locus_edited.svg +++ /dev/null @@ -1,64 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - version="1.1" - width="200mm" - height="200mm" - viewBox="0 0 200 200" - id="svg2" - sodipodi:docname="cccie31_locus_edited.svg" - inkscape:version="0.92.2 (5c3e80d, 2017-08-06)" - inkscape:export-filename="/home/user/admin/site/jaseg.net/cccie31_locus_edited.png" - inkscape:export-xdpi="304.79999" - inkscape:export-ydpi="304.79999"> - <metadata - id="metadata853"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <defs - id="defs851" /> - <sodipodi:namedview - pagecolor="#757575" - bordercolor="#666666" - borderopacity="1" - objecttolerance="10" - gridtolerance="10" - guidetolerance="10" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:window-width="2560" - inkscape:window-height="1394" - id="namedview849" - showgrid="false" - inkscape:zoom="1.2488333" - inkscape:cx="360.76879" - inkscape:cy="388.84643" - inkscape:window-x="0" - inkscape:window-y="46" - inkscape:window-maximized="0" - inkscape:current-layer="svg2" /> - <g - id="layer1" - style="stroke:#ffffff;stroke-width:1.99999999;stroke-miterlimit:4;stroke-dasharray:none"> - <path - id="path2991" - style="fill:none;stroke:#ffffff;stroke-width:1.99999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" - d="M 0,0 35.112,1.0588 35.0322,1.0512 34.9642,1.0442 34.902,1.0364 34.8224,0.9928 34.8016,0.9962 34.7602,0.983 34.712,0.9846 34.6674,0.9594 34.6042,0.955 34.5154,0.9598 34.4174,0.9666 34.2814,1.0204 34.0602,1.1578 33.7756,1.38 c 0,0 -0.267304,0.2174127 -0.3966,0.3312 -0.169453,0.1491275 -0.32941,0.3087398 -0.4966,0.4604 -0.218329,0.1980491 -0.44032,0.3920267 -0.6614,0.587 -0.296779,0.2617334 -0.596218,0.5204434 -0.8928,0.7824 -0.378371,0.3341961 -0.763262,0.6613518 -1.1312,1.007 -0.480602,0.4514862 -0.959583,0.906368 -1.405,1.3926 -0.59584,0.6504392 -1.171969,1.3222562 -1.6914,2.0352 -0.833592,1.1441462 -1.579982,2.352683 -2.277,3.5848 -1.065823,1.884053 -2.018746,3.83302 -2.9048,5.808 -1.347399,3.003298 -2.530815,6.079839 -3.66,9.1718 -1.639123,4.488286 -3.18996,9.014122 -4.5176,13.6042 -1.798515,6.218045 -3.292537,12.524418 -4.663,18.8506 -1.6902833,7.8025 -3.1725772,15.654697 -4.3862,23.5454 -1.2834927,8.344981 -2.3919081,16.72724 -3.0584,25.144 -0.6129876,7.74108 -1.12418942,15.51912 -0.8618,23.28 0.21600103,6.38882 0.456064,12.86993 2.0022,19.0726 1.0752855,4.31375 2.1260697,8.97102 4.9964,12.366 1.7909835,2.11834 4.361518,3.85701 7.09,4.3574 2.660714,0.48796 5.409703,-0.65135 7.9718,-1.5192 2.865196,-0.97052 5.480049,-2.57761 8.1122,-4.0686 2.621924,-1.48519 5.147938,-3.13947 7.6308,-4.847 2.51447,-1.72927 4.948959,-3.57487 7.3488,-5.46 2.463182,-1.93488 4.857621,-3.95696 7.231,-6.001 2.427203,-2.0904 4.80184,-4.24154 7.1658,-6.4032 2.409363,-2.20318 4.783617,-4.44462 7.1518,-6.692 2.398672,-2.27632 4.774303,-4.57685 7.1478,-6.8794 2.384784,-2.3135 4.757064,-4.63988 7.1268,-6.9688 2.360924,-2.32026 4.713931,-4.64856 7.0652,-6.9786 2.31795,-2.29702 4.630032,-4.59996 6.9426,-6.9024 2.248988,-2.23914 4.49396,-4.482308 6.7422,-6.7222 2.1526,-2.144606 4.30633,-4.288075 6.4602,-6.4314 2.02382,-2.013913 4.05037,-4.025084 6.0728,-6.0404 1.85382,-1.847306 3.70317,-3.699103 5.5564,-5.547 1.60598,-1.601356 3.2136,-3.201063 4.8208,-4.8012 1.41284,-1.406631 2.82484,-2.814105 4.2392,-4.2192 1.16766,-1.160011 2.33887,-2.31645 3.5062,-3.4768 0.95549,-0.949775 1.9091,-1.901433 2.863,-2.8528 0.76201,-0.759987 1.52239,-1.521614 2.285,-2.281 0.60605,-0.603487 1.21383,-1.205237 1.8204,-1.8082 0.48765,-0.484749 0.97525,-0.969551 1.4624,-1.4548 0.40786,-0.406269 0.81532,-0.812942 1.2228,-1.2196 0.3335,-0.332832 0.66667,-0.666 1.0002,-0.9988 0.26649,-0.265907 0.53295,-0.531852 0.7998,-0.7974 0.19715,-0.196186 0.395,-0.391666 0.592,-0.588 0.15226,-0.151742 0.304,-0.304 0.456,-0.456 0.11313,-0.113133 0.22627,-0.226267 0.3394,-0.3394 0.0747,-0.07467 0.14933,-0.149333 0.224,-0.224 0.0603,-0.06027 0.12053,-0.120533 0.1808,-0.1808 0.0484,-0.0484 0.0968,-0.0968 0.1452,-0.1452 0.0465,-0.04653 0.0931,-0.09307 0.1396,-0.1396 0.042,-0.042 0.084,-0.084 0.126,-0.126 0.0229,-0.02287 0.0457,-0.04573 0.0686,-0.0686 0.0135,-0.01347 0.0269,-0.02693 0.0404,-0.0404 0.007,-0.0065 0.0196,-0.0196 0.0196,-0.0196 v 0 0 l -0.0284,0.0284 0.0284,-0.0284 v 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" - inkscape:connector-curvature="0" - sodipodi:nodetypes="ccccccccccccccccsssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssc" /> - </g> -</svg> diff --git a/research/colorspace/colorsys_led_mapping.blend b/research/colorspace/colorsys_led_mapping.blend Binary files differdeleted file mode 100644 index c2ddbff..0000000 --- a/research/colorspace/colorsys_led_mapping.blend +++ /dev/null diff --git a/research/colorspace/colorsys_led_mapping.blend1 b/research/colorspace/colorsys_led_mapping.blend1 Binary files differdeleted file mode 100644 index e7819f9..0000000 --- a/research/colorspace/colorsys_led_mapping.blend1 +++ /dev/null diff --git a/research/colorspace/csv_to_svg_path.py b/research/colorspace/csv_to_svg_path.py deleted file mode 100755 index 5f638ed..0000000 --- a/research/colorspace/csv_to_svg_path.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python3 -import textwrap - -def svg_path_from_points(points, r): - points_joined = ' '.join(f'{x:.6f} {y:.6f}' for x, y in points) - return textwrap.dedent(f''' - <?xml version="1.0" encoding="UTF-8" standalone="no"?> - <svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - version="1.1" - width="{r*2}mm" - height="{r*2}mm" - viewBox="0 0 {r*2} {r*2}" - id="svg2"> - <g id="layer1"> - <path id="path2991" - style="fill:none;stroke:#000000;stroke-width:0.1mm;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="M 0,0 L {points_joined}" /> - </g> - </svg> - ''').strip() - -if __name__ == '__main__': - import argparse - parser = argparse.ArgumentParser() - parser.add_argument('locus_csv', help='CSV file containing locus coordinates. Format: lambda, x, y, z.') - parser.add_argument('-r', '--radius', type=float, default=100, help='Radius of plot area in mm') - args = parser.parse_args() - - import csv, ast - points = [] - with open(args.locus_csv, newline='') as f: - for row in csv.reader(f): - # use literal_eval to handle entries like "1.153E-5" - λ, x, y, z = (ast.literal_eval(e.strip()) for e in row) - points.append((x*args.radius*2, y*args.radius*2)) - - print(svg_path_from_points(points, args.radius)) diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000..9858785 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" + xmlns:xhtml="http://www.w3.org/1999/xhtml"> + <url> + <loc>http://jaseg.de/</loc> + <lastmod>2023-10-04T23:42:00+02:00</lastmod> + </url><url> + <loc>http://jaseg.de/projects/kimesh/</loc> + <lastmod>2023-10-04T23:42:00+02:00</lastmod> + </url><url> + <loc>http://jaseg.de/projects/</loc> + <lastmod>2023-10-04T23:42:00+02:00</lastmod> + </url><url> + <loc>http://jaseg.de/blog/</loc> + <lastmod>2022-02-21T20:00:00+01:00</lastmod> + </url><url> + <loc>http://jaseg.de/blog/telekom-gpon-sfp/</loc> + <lastmod>2022-02-21T20:00:00+01:00</lastmod> + </url><url> + <loc>http://jaseg.de/blog/ihsm-worlds-first-diy-hsm/</loc> + <lastmod>2021-11-23T23:42:20+01:00</lastmod> + </url><url> + <loc>http://jaseg.de/blog/kicad-mesh-plugin/</loc> + <lastmod>2020-08-18T13:15:39+02:00</lastmod> + </url><url> + <loc>http://jaseg.de/blog/private-contact-discovery/</loc> + <lastmod>2019-06-22T10:30:00+08:00</lastmod> + </url><url> + <loc>http://jaseg.de/blog/hsm-basics/</loc> + <lastmod>2019-05-17T15:29:20+08:00</lastmod> + </url><url> + <loc>http://jaseg.de/blog/serial-protocols/</loc> + <lastmod>2018-05-19T08:09:46+02:00</lastmod> + </url><url> + <loc>http://jaseg.de/blog/thors-hammer/</loc> + <lastmod>2018-05-03T11:59:37+02:00</lastmod> + </url><url> + <loc>http://jaseg.de/blog/multichannel-led-driver/</loc> + <lastmod>2018-05-02T11:31:14+02:00</lastmod> + </url><url> + <loc>http://jaseg.de/blog/wifi-led-driver/</loc> + <lastmod>2018-05-02T11:31:03+02:00</lastmod> + </url><url> + <loc>http://jaseg.de/blog/led-characterization/</loc> + <lastmod>2018-05-02T11:18:38+02:00</lastmod> + </url><url> + <loc>http://jaseg.de/about/</loc> + </url><url> + <loc>http://jaseg.de/categories/</loc> + </url><url> + <loc>http://jaseg.de/projects/gerbolyze/</loc> + </url><url> + <loc>http://jaseg.de/projects/gerbonara/</loc> + </url><url> + <loc>http://jaseg.de/imprint/</loc> + </url><url> + <loc>http://jaseg.de/projects/lolcat-c/</loc> + </url><url> + <loc>http://jaseg.de/posts/</loc> + </url><url> + <loc>http://jaseg.de/projects/python-mpv/</loc> + </url><url> + <loc>http://jaseg.de/projects/svg-flatten/</loc> + </url><url> + <loc>http://jaseg.de/projects/wsdiff/</loc> + </url> +</urlset> diff --git a/themes/conspiracy/assets/css/style.css b/style.css index 831e4cb..f235a40 100644 --- a/themes/conspiracy/assets/css/style.css +++ b/style.css @@ -23,97 +23,97 @@ html { /* @font-face { font-family: "Manuskript Gothisch"; - src: url("{{ (resources.Get "fonts/manuskript_gothisch/Manuskript Gothisch UNZ1A.ttf").RelPermalink }}"); + src: url("/fonts/manuskript_gothisch/Manuskript%20Gothisch%20UNZ1A.ttf"); } */ @font-face { font-family: "Nyght Serif"; - src: url("{{ (resources.Get "fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Light.woff2").RelPermalink }}"); + src: url("/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Light.woff2"); font-weight: 100; } @font-face { font-family: "Nyght Serif"; - src: url("{{ (resources.Get "fonts/nyght-serif-main/fonts/WEB/NyghtSerif-LightItalic.woff2").RelPermalink }}"); + src: url("/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-LightItalic.woff2"); font-weight: 100; font-style: italic; } @font-face { font-family: "Nyght Serif"; - src: url("{{ (resources.Get "fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2").RelPermalink }}"); + src: url("/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2"); font-weight: 400; } @font-face { font-family: "Nyght Serif"; - src: url("{{ (resources.Get "fonts/nyght-serif-main/fonts/WEB/NyghtSerif-RegularItalic.woff2").RelPermalink }}"); + src: url("/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-RegularItalic.woff2"); font-weight: 400; font-style: italic; } @font-face { font-family: "Nyght Serif"; - src: url("{{ (resources.Get "fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Medium.woff2").RelPermalink }}"); + src: url("/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Medium.woff2"); font-weight: 500; } @font-face { font-family: "Nyght Serif"; - src: url("{{ (resources.Get "fonts/nyght-serif-main/fonts/WEB/NyghtSerif-MediumItalic.woff2").RelPermalink }}"); + src: url("/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-MediumItalic.woff2"); font-weight: 500; font-style: italic; } @font-face { font-family: "Nyght Serif"; - src: url("{{ (resources.Get "fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2").RelPermalink }}"); + src: url("/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2"); font-weight: 700; } @font-face { font-family: "Nyght Serif"; - src: url("{{ (resources.Get "fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2").RelPermalink }}"); + src: url("/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2"); font-weight: 700; font-style: italic; } @font-face { font-family: "Nyght Serif"; - src: url("{{ (resources.Get "fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Dark.woff2").RelPermalink }}"); + src: url("/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Dark.woff2"); font-weight: 900; } @font-face { font-family: "Nyght Serif"; - src: url("{{ (resources.Get "fonts/nyght-serif-main/fonts/WEB/NyghtSerif-DarkItalic.woff2").RelPermalink }}"); + src: url("/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-DarkItalic.woff2"); font-weight: 900; font-style: italic; } @font-face { font-family: "Roboto Slab"; - src: url("{{ (resources.Get "fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf").RelPermalink }}") format("truetype-variations"); + src: url("/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf") format("truetype-variations"); font-weight: 100 200 300 400 500 600 700 800 900; } /* @font-face { font-family: "Bodoni Moda"; - src: url("{{ (resources.Get "fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-MediumItalic.ttf").RelPermalink }}"); + src: url("/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-MediumItalic.ttf"); font-style: italic; } @font-face { font-family: "Bodoni Moda"; - src: url("{{ (resources.Get "fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Medium.ttf").RelPermalink }}"); + src: url("/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Medium.ttf"); } */ @font-face { font-family: "Fira Code"; - src: url("{{ (resources.Get "fonts/fira_code/FiraCode-VariableFont_wght.ttf").RelPermalink }}") format("truetype-variations"); + src: url("/fonts/fira_code/FiraCode-VariableFont_wght.ttf") format("truetype-variations"); font-weight: 300 400 500 600 700; } @@ -144,7 +144,7 @@ body::before { background: linear-gradient(to bottom, hsl(0 0% 8% / 80%), hsl(0 0% 3% / 95%)), linear-gradient(hsl(220 15% 18%), hsl(220 15% 18%)), - center 20% / cover space no-repeat fixed url("{{ (resources.Get "images/fabrizio-conti-TUmjK7ZJgbI-unsplash.jpg").RelPermalink }}"), + center 20% / cover space no-repeat fixed url("/images/fabrizio-conti-TUmjK7ZJgbI-unsplash.jpg"), var(--c-bg-backdrop); background-blend-mode: luminosity, color, normal; } @@ -627,7 +627,7 @@ body .il { color: var(--c-text); font-weight: 600 } /* Literal.Number.Integer.L background: linear-gradient(to bottom, hsl(0 0% 100% / 70%), hsl(0 0% 90% / 80%)), linear-gradient(hsl(260 10% 10%), hsl(260 10% 10%)), - center 20% / cover space no-repeat fixed url("{{ (resources.Get "images/fabrizio-conti-TUmjK7ZJgbI-unsplash.jpg").RelPermalink }}"), + center 20% / cover space no-repeat fixed url("/images/fabrizio-conti-TUmjK7ZJgbI-unsplash.jpg"), var(--c-bg-backdrop); } diff --git a/themes/conspiracy/LICENSE b/themes/conspiracy/LICENSE deleted file mode 100644 index 17993f6..0000000 --- a/themes/conspiracy/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2023 YOUR_NAME_HERE - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/themes/conspiracy/archetypes/default.md b/themes/conspiracy/archetypes/default.md deleted file mode 100644 index ac36e06..0000000 --- a/themes/conspiracy/archetypes/default.md +++ /dev/null @@ -1,2 +0,0 @@ -+++ -+++ diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/BodoniModa-Italic-VariableFont_opsz,wght.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/BodoniModa-Italic-VariableFont_opsz,wght.ttf Binary files differdeleted file mode 100644 index c07e0ea..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/BodoniModa-Italic-VariableFont_opsz,wght.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/BodoniModa-VariableFont_opsz,wght.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/BodoniModa-VariableFont_opsz,wght.ttf Binary files differdeleted file mode 100644 index f943195..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/BodoniModa-VariableFont_opsz,wght.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/OFL.txt b/themes/conspiracy/assets/fonts/bodoni_moda/OFL.txt deleted file mode 100644 index ac5c663..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2020 The Bodoni Moda Project Authors (https://github.com/indestructible-type/Bodoni)
-
-This Font Software is licensed under the SIL Open Font License, Version 1.1.
-This license is copied below, and is also available with a FAQ at:
-http://scripts.sil.org/OFL
-
-
------------------------------------------------------------
-SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
------------------------------------------------------------
-
-PREAMBLE
-The goals of the Open Font License (OFL) are to stimulate worldwide
-development of collaborative font projects, to support the font creation
-efforts of academic and linguistic communities, and to provide a free and
-open framework in which fonts may be shared and improved in partnership
-with others.
-
-The OFL allows the licensed fonts to be used, studied, modified and
-redistributed freely as long as they are not sold by themselves. The
-fonts, including any derivative works, can be bundled, embedded,
-redistributed and/or sold with any software provided that any reserved
-names are not used by derivative works. The fonts and derivatives,
-however, cannot be released under any other type of license. The
-requirement for fonts to remain under this license does not apply
-to any document created using the fonts or their derivatives.
-
-DEFINITIONS
-"Font Software" refers to the set of files released by the Copyright
-Holder(s) under this license and clearly marked as such. This may
-include source files, build scripts and documentation.
-
-"Reserved Font Name" refers to any names specified as such after the
-copyright statement(s).
-
-"Original Version" refers to the collection of Font Software components as
-distributed by the Copyright Holder(s).
-
-"Modified Version" refers to any derivative made by adding to, deleting,
-or substituting -- in part or in whole -- any of the components of the
-Original Version, by changing formats or by porting the Font Software to a
-new environment.
-
-"Author" refers to any designer, engineer, programmer, technical
-writer or other person who contributed to the Font Software.
-
-PERMISSION & CONDITIONS
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of the Font Software, to use, study, copy, merge, embed, modify,
-redistribute, and sell modified and unmodified copies of the Font
-Software, subject to the following conditions:
-
-1) Neither the Font Software nor any of its individual components,
-in Original or Modified Versions, may be sold by itself.
-
-2) Original or Modified Versions of the Font Software may be bundled,
-redistributed and/or sold with any software, provided that each copy
-contains the above copyright notice and this license. These can be
-included either as stand-alone text files, human-readable headers or
-in the appropriate machine-readable metadata fields within text or
-binary files as long as those fields can be easily viewed by the user.
-
-3) No Modified Version of the Font Software may use the Reserved Font
-Name(s) unless explicit written permission is granted by the corresponding
-Copyright Holder. This restriction only applies to the primary font name as
-presented to the users.
-
-4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
-Software shall not be used to promote, endorse or advertise any
-Modified Version, except to acknowledge the contribution(s) of the
-Copyright Holder(s) and the Author(s) or with their explicit written
-permission.
-
-5) The Font Software, modified or unmodified, in part or in whole,
-must be distributed entirely under this license, and must not be
-distributed under any other license. The requirement for fonts to
-remain under this license does not apply to any document created
-using the Font Software.
-
-TERMINATION
-This license becomes null and void if any of the above conditions are
-not met.
-
-DISCLAIMER
-THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
-OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
-COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
-DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
-OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/README.txt b/themes/conspiracy/assets/fonts/bodoni_moda/README.txt deleted file mode 100644 index d0fc50f..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/README.txt +++ /dev/null @@ -1,124 +0,0 @@ -Bodoni Moda Variable Font -========================= - -This download contains Bodoni Moda as both variable fonts and static fonts. - -Bodoni Moda is a variable font with these axes: - opsz - wght - -This means all the styles are contained in these files: - BodoniModa-VariableFont_opsz,wght.ttf - BodoniModa-Italic-VariableFont_opsz,wght.ttf - -If your app fully supports variable fonts, you can now pick intermediate styles -that aren’t available as static fonts. Not all apps support variable fonts, and -in those cases you can use the static font files for Bodoni Moda: - static/BodoniModa_9pt/BodoniModa_9pt-Regular.ttf - static/BodoniModa_9pt/BodoniModa_9pt-Medium.ttf - static/BodoniModa_9pt/BodoniModa_9pt-SemiBold.ttf - static/BodoniModa_9pt/BodoniModa_9pt-Bold.ttf - static/BodoniModa_9pt/BodoniModa_9pt-ExtraBold.ttf - static/BodoniModa_9pt/BodoniModa_9pt-Black.ttf - static/BodoniModa_18pt/BodoniModa_18pt-Regular.ttf - static/BodoniModa_18pt/BodoniModa_18pt-Medium.ttf - static/BodoniModa_18pt/BodoniModa_18pt-SemiBold.ttf - static/BodoniModa_18pt/BodoniModa_18pt-Bold.ttf - static/BodoniModa_18pt/BodoniModa_18pt-ExtraBold.ttf - static/BodoniModa_18pt/BodoniModa_18pt-Black.ttf - static/BodoniModa_28pt/BodoniModa_28pt-Regular.ttf - static/BodoniModa_28pt/BodoniModa_28pt-Medium.ttf - static/BodoniModa_28pt/BodoniModa_28pt-SemiBold.ttf - static/BodoniModa_28pt/BodoniModa_28pt-Bold.ttf - static/BodoniModa_28pt/BodoniModa_28pt-ExtraBold.ttf - static/BodoniModa_28pt/BodoniModa_28pt-Black.ttf - static/BodoniModa_48pt/BodoniModa_48pt-Regular.ttf - static/BodoniModa_48pt/BodoniModa_48pt-Medium.ttf - static/BodoniModa_48pt/BodoniModa_48pt-SemiBold.ttf - static/BodoniModa_48pt/BodoniModa_48pt-Bold.ttf - static/BodoniModa_48pt/BodoniModa_48pt-ExtraBold.ttf - static/BodoniModa_48pt/BodoniModa_48pt-Black.ttf - static/BodoniModa_72pt/BodoniModa_72pt-Regular.ttf - static/BodoniModa_72pt/BodoniModa_72pt-Medium.ttf - static/BodoniModa_72pt/BodoniModa_72pt-SemiBold.ttf - static/BodoniModa_72pt/BodoniModa_72pt-Bold.ttf - static/BodoniModa_72pt/BodoniModa_72pt-ExtraBold.ttf - static/BodoniModa_72pt/BodoniModa_72pt-Black.ttf - static/BodoniModa_9pt/BodoniModa_9pt-Italic.ttf - static/BodoniModa_9pt/BodoniModa_9pt-MediumItalic.ttf - static/BodoniModa_9pt/BodoniModa_9pt-SemiBoldItalic.ttf - static/BodoniModa_9pt/BodoniModa_9pt-BoldItalic.ttf - static/BodoniModa_9pt/BodoniModa_9pt-ExtraBoldItalic.ttf - static/BodoniModa_9pt/BodoniModa_9pt-BlackItalic.ttf - static/BodoniModa_18pt/BodoniModa_18pt-Italic.ttf - static/BodoniModa_18pt/BodoniModa_18pt-MediumItalic.ttf - static/BodoniModa_18pt/BodoniModa_18pt-SemiBoldItalic.ttf - static/BodoniModa_18pt/BodoniModa_18pt-BoldItalic.ttf - static/BodoniModa_18pt/BodoniModa_18pt-ExtraBoldItalic.ttf - static/BodoniModa_18pt/BodoniModa_18pt-BlackItalic.ttf - static/BodoniModa_28pt/BodoniModa_28pt-Italic.ttf - static/BodoniModa_28pt/BodoniModa_28pt-MediumItalic.ttf - static/BodoniModa_28pt/BodoniModa_28pt-SemiBoldItalic.ttf - static/BodoniModa_28pt/BodoniModa_28pt-BoldItalic.ttf - static/BodoniModa_28pt/BodoniModa_28pt-ExtraBoldItalic.ttf - static/BodoniModa_28pt/BodoniModa_28pt-BlackItalic.ttf - static/BodoniModa_48pt/BodoniModa_48pt-Italic.ttf - static/BodoniModa_48pt/BodoniModa_48pt-MediumItalic.ttf - static/BodoniModa_48pt/BodoniModa_48pt-SemiBoldItalic.ttf - static/BodoniModa_48pt/BodoniModa_48pt-BoldItalic.ttf - static/BodoniModa_48pt/BodoniModa_48pt-ExtraBoldItalic.ttf - static/BodoniModa_48pt/BodoniModa_48pt-BlackItalic.ttf - static/BodoniModa_72pt/BodoniModa_72pt-Italic.ttf - static/BodoniModa_72pt/BodoniModa_72pt-MediumItalic.ttf - static/BodoniModa_72pt/BodoniModa_72pt-SemiBoldItalic.ttf - static/BodoniModa_72pt/BodoniModa_72pt-BoldItalic.ttf - static/BodoniModa_72pt/BodoniModa_72pt-ExtraBoldItalic.ttf - static/BodoniModa_72pt/BodoniModa_72pt-BlackItalic.ttf - -Get started ------------ - -1. Install the font files you want to use - -2. Use your app's font picker to view the font family and all the -available styles - -Learn more about variable fonts -------------------------------- - - https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts - https://variablefonts.typenetwork.com - https://medium.com/variable-fonts - -In desktop apps - - https://theblog.adobe.com/can-variable-fonts-illustrator-cc - https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts - -Online - - https://developers.google.com/fonts/docs/getting_started - https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide - https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts - -Installing fonts - - MacOS: https://support.apple.com/en-us/HT201749 - Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux - Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows - -Android Apps - - https://developers.google.com/fonts/docs/android - https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts - -License -------- -Please read the full license text (OFL.txt) to understand the permissions, -restrictions and requirements for usage, redistribution, and modification. - -You can use them in your products & projects – print or digital, -commercial or otherwise. - -This isn't legal advice, please consider consulting a lawyer and see the full -license for all details. diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Black.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Black.ttf Binary files differdeleted file mode 100644 index 185d01d..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Black.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-BlackItalic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-BlackItalic.ttf Binary files differdeleted file mode 100644 index 0135e30..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-BlackItalic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Bold.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Bold.ttf Binary files differdeleted file mode 100644 index f47dfbf..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Bold.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-BoldItalic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-BoldItalic.ttf Binary files differdeleted file mode 100644 index aa8c674..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-BoldItalic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-ExtraBold.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-ExtraBold.ttf Binary files differdeleted file mode 100644 index 6f20a92..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-ExtraBold.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-ExtraBoldItalic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-ExtraBoldItalic.ttf Binary files differdeleted file mode 100644 index fd47a26..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-ExtraBoldItalic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Italic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Italic.ttf Binary files differdeleted file mode 100644 index a5e3586..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Italic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-MediumItalic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-MediumItalic.ttf Binary files differdeleted file mode 100644 index becd9ad..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-MediumItalic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Regular.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Regular.ttf Binary files differdeleted file mode 100644 index 508beea..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Regular.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-SemiBold.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-SemiBold.ttf Binary files differdeleted file mode 100644 index 65533fa..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-SemiBold.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-SemiBoldItalic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-SemiBoldItalic.ttf Binary files differdeleted file mode 100644 index 91b43ed..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-SemiBoldItalic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-Black.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-Black.ttf Binary files differdeleted file mode 100644 index eba4705..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-Black.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-BlackItalic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-BlackItalic.ttf Binary files differdeleted file mode 100644 index 6e44d6a..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-BlackItalic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-Bold.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-Bold.ttf Binary files differdeleted file mode 100644 index c1fa8ab..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-Bold.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-BoldItalic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-BoldItalic.ttf Binary files differdeleted file mode 100644 index fd03a9e..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-BoldItalic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-ExtraBold.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-ExtraBold.ttf Binary files differdeleted file mode 100644 index 2a1297d..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-ExtraBold.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-ExtraBoldItalic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-ExtraBoldItalic.ttf Binary files differdeleted file mode 100644 index 18ff90f..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-ExtraBoldItalic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-Italic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-Italic.ttf Binary files differdeleted file mode 100644 index fc6e85b..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-Italic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-Medium.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-Medium.ttf Binary files differdeleted file mode 100644 index da3c186..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-Medium.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-MediumItalic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-MediumItalic.ttf Binary files differdeleted file mode 100644 index 024c962..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-MediumItalic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-Regular.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-Regular.ttf Binary files differdeleted file mode 100644 index 469edd3..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-Regular.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-SemiBold.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-SemiBold.ttf Binary files differdeleted file mode 100644 index 43849f4..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-SemiBold.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-SemiBoldItalic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-SemiBoldItalic.ttf Binary files differdeleted file mode 100644 index 90cf8c5..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_28pt/BodoniModa_28pt-SemiBoldItalic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-Black.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-Black.ttf Binary files differdeleted file mode 100644 index 839aae8..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-Black.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-BlackItalic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-BlackItalic.ttf Binary files differdeleted file mode 100644 index 79c3ac0..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-BlackItalic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-Bold.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-Bold.ttf Binary files differdeleted file mode 100644 index 2da9788..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-Bold.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-BoldItalic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-BoldItalic.ttf Binary files differdeleted file mode 100644 index a819d79..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-BoldItalic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-ExtraBold.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-ExtraBold.ttf Binary files differdeleted file mode 100644 index d44e897..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-ExtraBold.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-ExtraBoldItalic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-ExtraBoldItalic.ttf Binary files differdeleted file mode 100644 index 065496b..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-ExtraBoldItalic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-Italic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-Italic.ttf Binary files differdeleted file mode 100644 index 011b785..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-Italic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-Medium.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-Medium.ttf Binary files differdeleted file mode 100644 index b452607..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-Medium.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-MediumItalic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-MediumItalic.ttf Binary files differdeleted file mode 100644 index 6adcb7c..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-MediumItalic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-Regular.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-Regular.ttf Binary files differdeleted file mode 100644 index 591f543..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-Regular.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-SemiBold.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-SemiBold.ttf Binary files differdeleted file mode 100644 index 26f1e0f..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-SemiBold.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-SemiBoldItalic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-SemiBoldItalic.ttf Binary files differdeleted file mode 100644 index 1331fbc..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_48pt/BodoniModa_48pt-SemiBoldItalic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-Black.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-Black.ttf Binary files differdeleted file mode 100644 index a4afc93..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-Black.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-BlackItalic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-BlackItalic.ttf Binary files differdeleted file mode 100644 index d177644..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-BlackItalic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-Bold.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-Bold.ttf Binary files differdeleted file mode 100644 index 6bd141d..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-Bold.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-BoldItalic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-BoldItalic.ttf Binary files differdeleted file mode 100644 index 2e3df3d..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-BoldItalic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-ExtraBold.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-ExtraBold.ttf Binary files differdeleted file mode 100644 index 0652136..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-ExtraBold.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-ExtraBoldItalic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-ExtraBoldItalic.ttf Binary files differdeleted file mode 100644 index f0abc45..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-ExtraBoldItalic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-Italic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-Italic.ttf Binary files differdeleted file mode 100644 index 157066c..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-Italic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-Medium.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-Medium.ttf Binary files differdeleted file mode 100644 index df6c77e..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-Medium.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-MediumItalic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-MediumItalic.ttf Binary files differdeleted file mode 100644 index 487d436..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-MediumItalic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-Regular.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-Regular.ttf Binary files differdeleted file mode 100644 index 599ef60..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-Regular.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-SemiBold.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-SemiBold.ttf Binary files differdeleted file mode 100644 index 637df88..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-SemiBold.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-SemiBoldItalic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-SemiBoldItalic.ttf Binary files differdeleted file mode 100644 index 831a4c5..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_72pt/BodoniModa_72pt-SemiBoldItalic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-Black.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-Black.ttf Binary files differdeleted file mode 100644 index 157ba8c..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-Black.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-BlackItalic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-BlackItalic.ttf Binary files differdeleted file mode 100644 index ff3e3cf..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-BlackItalic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-Bold.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-Bold.ttf Binary files differdeleted file mode 100644 index cb232c8..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-Bold.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-BoldItalic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-BoldItalic.ttf Binary files differdeleted file mode 100644 index d838ee6..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-BoldItalic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-ExtraBold.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-ExtraBold.ttf Binary files differdeleted file mode 100644 index 6d7cd44..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-ExtraBold.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-ExtraBoldItalic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-ExtraBoldItalic.ttf Binary files differdeleted file mode 100644 index 7d3195d..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-ExtraBoldItalic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-Italic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-Italic.ttf Binary files differdeleted file mode 100644 index eaf7aaf..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-Italic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-Medium.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-Medium.ttf Binary files differdeleted file mode 100644 index 76747c2..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-Medium.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-Regular.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-Regular.ttf Binary files differdeleted file mode 100644 index a85bcac..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-Regular.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-SemiBold.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-SemiBold.ttf Binary files differdeleted file mode 100644 index 510f964..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-SemiBold.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-SemiBoldItalic.ttf b/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-SemiBoldItalic.ttf Binary files differdeleted file mode 100644 index 77f97e9..0000000 --- a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-SemiBoldItalic.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/fira_code/OFL.txt b/themes/conspiracy/assets/fonts/fira_code/OFL.txt deleted file mode 100644 index 19f4298..0000000 --- a/themes/conspiracy/assets/fonts/fira_code/OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2014-2020 The Fira Code Project Authors (https://github.com/tonsky/FiraCode)
-
-This Font Software is licensed under the SIL Open Font License, Version 1.1.
-This license is copied below, and is also available with a FAQ at:
-http://scripts.sil.org/OFL
-
-
------------------------------------------------------------
-SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
------------------------------------------------------------
-
-PREAMBLE
-The goals of the Open Font License (OFL) are to stimulate worldwide
-development of collaborative font projects, to support the font creation
-efforts of academic and linguistic communities, and to provide a free and
-open framework in which fonts may be shared and improved in partnership
-with others.
-
-The OFL allows the licensed fonts to be used, studied, modified and
-redistributed freely as long as they are not sold by themselves. The
-fonts, including any derivative works, can be bundled, embedded,
-redistributed and/or sold with any software provided that any reserved
-names are not used by derivative works. The fonts and derivatives,
-however, cannot be released under any other type of license. The
-requirement for fonts to remain under this license does not apply
-to any document created using the fonts or their derivatives.
-
-DEFINITIONS
-"Font Software" refers to the set of files released by the Copyright
-Holder(s) under this license and clearly marked as such. This may
-include source files, build scripts and documentation.
-
-"Reserved Font Name" refers to any names specified as such after the
-copyright statement(s).
-
-"Original Version" refers to the collection of Font Software components as
-distributed by the Copyright Holder(s).
-
-"Modified Version" refers to any derivative made by adding to, deleting,
-or substituting -- in part or in whole -- any of the components of the
-Original Version, by changing formats or by porting the Font Software to a
-new environment.
-
-"Author" refers to any designer, engineer, programmer, technical
-writer or other person who contributed to the Font Software.
-
-PERMISSION & CONDITIONS
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of the Font Software, to use, study, copy, merge, embed, modify,
-redistribute, and sell modified and unmodified copies of the Font
-Software, subject to the following conditions:
-
-1) Neither the Font Software nor any of its individual components,
-in Original or Modified Versions, may be sold by itself.
-
-2) Original or Modified Versions of the Font Software may be bundled,
-redistributed and/or sold with any software, provided that each copy
-contains the above copyright notice and this license. These can be
-included either as stand-alone text files, human-readable headers or
-in the appropriate machine-readable metadata fields within text or
-binary files as long as those fields can be easily viewed by the user.
-
-3) No Modified Version of the Font Software may use the Reserved Font
-Name(s) unless explicit written permission is granted by the corresponding
-Copyright Holder. This restriction only applies to the primary font name as
-presented to the users.
-
-4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
-Software shall not be used to promote, endorse or advertise any
-Modified Version, except to acknowledge the contribution(s) of the
-Copyright Holder(s) and the Author(s) or with their explicit written
-permission.
-
-5) The Font Software, modified or unmodified, in part or in whole,
-must be distributed entirely under this license, and must not be
-distributed under any other license. The requirement for fonts to
-remain under this license does not apply to any document created
-using the Font Software.
-
-TERMINATION
-This license becomes null and void if any of the above conditions are
-not met.
-
-DISCLAIMER
-THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
-OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
-COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
-DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
-OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/themes/conspiracy/assets/fonts/fira_code/README.txt b/themes/conspiracy/assets/fonts/fira_code/README.txt deleted file mode 100644 index e70b485..0000000 --- a/themes/conspiracy/assets/fonts/fira_code/README.txt +++ /dev/null @@ -1,67 +0,0 @@ -Fira Code Variable Font -======================= - -This download contains Fira Code as both a variable font and static fonts. - -Fira Code is a variable font with this axis: - wght - -This means all the styles are contained in a single file: - FiraCode-VariableFont_wght.ttf - -If your app fully supports variable fonts, you can now pick intermediate styles -that aren’t available as static fonts. Not all apps support variable fonts, and -in those cases you can use the static font files for Fira Code: - static/FiraCode-Light.ttf - static/FiraCode-Regular.ttf - static/FiraCode-Medium.ttf - static/FiraCode-SemiBold.ttf - static/FiraCode-Bold.ttf - -Get started ------------ - -1. Install the font files you want to use - -2. Use your app's font picker to view the font family and all the -available styles - -Learn more about variable fonts -------------------------------- - - https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts - https://variablefonts.typenetwork.com - https://medium.com/variable-fonts - -In desktop apps - - https://theblog.adobe.com/can-variable-fonts-illustrator-cc - https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts - -Online - - https://developers.google.com/fonts/docs/getting_started - https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide - https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts - -Installing fonts - - MacOS: https://support.apple.com/en-us/HT201749 - Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux - Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows - -Android Apps - - https://developers.google.com/fonts/docs/android - https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts - -License -------- -Please read the full license text (OFL.txt) to understand the permissions, -restrictions and requirements for usage, redistribution, and modification. - -You can use them in your products & projects – print or digital, -commercial or otherwise. - -This isn't legal advice, please consider consulting a lawyer and see the full -license for all details. diff --git a/themes/conspiracy/assets/fonts/fira_code/static/FiraCode-Bold.ttf b/themes/conspiracy/assets/fonts/fira_code/static/FiraCode-Bold.ttf Binary files differdeleted file mode 100644 index c0aa0f5..0000000 --- a/themes/conspiracy/assets/fonts/fira_code/static/FiraCode-Bold.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/fira_code/static/FiraCode-Light.ttf b/themes/conspiracy/assets/fonts/fira_code/static/FiraCode-Light.ttf Binary files differdeleted file mode 100644 index 84801e1..0000000 --- a/themes/conspiracy/assets/fonts/fira_code/static/FiraCode-Light.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/fira_code/static/FiraCode-Medium.ttf b/themes/conspiracy/assets/fonts/fira_code/static/FiraCode-Medium.ttf Binary files differdeleted file mode 100644 index 570b4d1..0000000 --- a/themes/conspiracy/assets/fonts/fira_code/static/FiraCode-Medium.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/fira_code/static/FiraCode-Regular.ttf b/themes/conspiracy/assets/fonts/fira_code/static/FiraCode-Regular.ttf Binary files differdeleted file mode 100644 index 82baafc..0000000 --- a/themes/conspiracy/assets/fonts/fira_code/static/FiraCode-Regular.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/fira_code/static/FiraCode-SemiBold.ttf b/themes/conspiracy/assets/fonts/fira_code/static/FiraCode-SemiBold.ttf Binary files differdeleted file mode 100644 index f3a34fa..0000000 --- a/themes/conspiracy/assets/fonts/fira_code/static/FiraCode-SemiBold.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/manuskript_gothisch/OFL-FAQ.txt b/themes/conspiracy/assets/fonts/manuskript_gothisch/OFL-FAQ.txt deleted file mode 100644 index 1b67d59..0000000 --- a/themes/conspiracy/assets/fonts/manuskript_gothisch/OFL-FAQ.txt +++ /dev/null @@ -1,242 +0,0 @@ -Eine kurze deutsche Zusammenfassung der OFL finden Sie auf:
-http://de.wikipedia.org/wiki/SIL_Open_Font_License
-
-
-
-OFL FAQ - Frequently Asked Questions about the SIL Open Font License (OFL)
-Version 1.1-update1 - 31 March 2009
-(See http://scripts.sil.org/OFL for updates)
-
-
-1 ABOUT USING AND DISTRIBUTING FONTS LICENSED UNDER THE OFL
-
-1.1 Can I use the fonts in any publication, even embedded in the file?
-Yes. You may use them like most other fonts, but unlike some fonts you may include an embedded subset of the fonts in your document. Such use does not require you to include this license or other files (listed in OFL condition 2), nor does it require any type of acknowledgement within the publication. Some mention of the font name within the publication information (such as in a colophon) is usually appreciated. If you wish to include the complete font as a separate file, you should distribute the full font package, including all existing acknowledgements, and comply with the OFL conditions. Of course, referencing or embedding an OFL font in any document does not change the license of the document itself. The requirement for fonts to remain under the OFL does not apply to any document created using the fonts and their derivatives. Similarly, creating any kind of graphic using a font under OFL does not make the resulting artwork subject to the OFL.
-
-1.2 Can I make web pages using these fonts?
-Yes! Go ahead! Using CSS (Cascading Style Sheets) is recommended. Direct usage of fonts retrieved from a remote server - also referred to as font linking - using cross-platform open standards like @font-face is encouraged. This is considered to be use and distribution for which the OFL explicitly grants permission. The font file itself is not embedded in the webpage but referenced through a web address (i.e. a URI regardless of file format or access protocol) which will cause the browser to retrieve and use the corresponding font to render the webpage. This usage scenario is different from font embedding within a document (i.e. a PDF) where the font or some of its elements become part of the document. Note that embedding in a document is also permitted by the license as indicated in 1.1. (See 1.10 for details related to URL-based access restrictions methods or DRM mechanisms).
-
-1.3 Can I make the fonts available to others from my web site?
-Yes, as long as you meet the conditions of the license (do not sell by itself, include the necessary files, include the necessary copyright and license information, rename Modified Versions, do not abuse the Author(s)' name(s) and do not sublicense). In the case where you are hosting fonts to be served on the web, make sure the file contains the needed copyright notice(s) and licensing information in its metadata. Please double-check the accuracy of every field to prevent contradictory information. If you are making the font available for use via the @font-face open standard, putting this information in the standard font metadata fields is sufficient. Other font formats, including EOT and proposed superior alternatives, already provide fields for this information.
-
-1.4 Can the fonts be included with Free/Libre and Open Source Software collections such as GNU/Linux and BSD distributions?
-Yes! Fonts licensed under the OFL can be freely aggregated with software under FLOSS (Free/Libre and Open Source Software) licenses. Since fonts are much more useful aggregated to than merged with existing software, possible incompatibility with existing software licenses is not a problem. You can also repackage the fonts and the accompanying components in a .rpm or .deb package and include them in distro CD/DVDs and online repositories.
-
-1.5 I want to distribute the fonts with my program. Does this mean my program also has to be free/libre and open source software?
-No. Only the portions based on the Font Software are required to be released under the OFL. The intent of the license is to allow aggregation or bundling with software under restricted licensing as well.
-
-1.6 Can I include the fonts on a CD of freeware or commercial fonts?
-Yes, as long some other font or software is also on the disk, so the OFL font is not sold by itself.
-
-1.7 Can I sell a software package that includes these fonts?
-Yes, you can do this with both the Original Version and a Modified Version. Examples of bundling made possible by the OFL would include: word processors, design and publishing applications, training and educational software, edutainment software, etc.
-
-1.8 Why won't the OFL let me sell the fonts alone?
-The intent is to keep people from making money by simply redistributing the fonts. The only people who ought to profit directly from the fonts should be the original authors, and those authors have kindly given up potential direct income to distribute their fonts under the OFL. Please honor and respect their contribution!
-
-1.9 I've come across a font released under the OFL. How can I easily get more information about the Original Version? How can I know where it stands compared to the Original Version or other Modified Versions?
-Consult the copyright statement(s) in the license for ways to contact the original authors. Consult the FONTLOG for information on how the font differs from the Original Version, and get in touch with the various contributors via the information in the acknowledgment section. Please consider using the Original Versions of the fonts whenever possible.
-
-1.10 What do you mean in condition 4? Can you provide examples of abusive promotion / endorsement / advertisement vs. normal acknowledgement?
-The intent is that the goodwill and reputation of the author(s) should not be used in a way that makes it sound like the original author(s) endorse or approve of a specific Modified Version or software bundle. For example, it would not be right to advertise a word processor by naming the author(s) in a listing of software features, or to promote a Modified Version on a web site by saying "designed by ...". However, it would be appropriate to acknowledge the author(s) if your software package has a list of people who deserve thanks. We realize that this can seem to be a gray area, but the standard used to judge an acknowledgement is that if the acknowledgement benefits the author(s) it is allowed, but if it primarily benefits other parties, or could reflect poorly on the author(s), then it is not.
-
-1.11 Can Font Software released under the OFL be subject to URL-based access restrictions methods or DRM mechanisms?
-Yes, but these issues are out-of-scope for the OFL. The license itself neither encourages their use nor prohibits them since such mechanisms are not implemented in the components of the Font Software but through external software. Such restrictions are put in place for many different purposes corresponding to various usage scenarios. One common example is to limit potentially dangerous cross-site scripting attacks. However, in the spirit of libre/open fonts and unrestricted writing systems, we strongly encourage open sharing and reuse of OFL fonts, and the establishment of an environment where such restrictions are unnecessary. Note that whether you wish to use such mechanisms or you prefer not to, you must still abide by the rules set forth by the OFL when using fonts released by their authors under this license. Derivative fonts must be licensed under the OFL, even if they are part of a service for which you charge fees and/or for which access to source code is restricted. You may not sell the fonts on their own - they must be part of a larger software package or bundle. For example, even if the OFL font is distributed in a software package or via an online service using a DRM mechanism, the user would still have the right to extract that font, use, study, modify and redistribute it under the OFL.
-
-1.12 What about distributing fonts with a document? Within a compressed folder structure like an OpenDocument file (.odt) for example? Is it redistribution, bundling or embedding?
-The vast majority of the time, documents circulated in electronic form reference a font name which will match the corresponding font on the target system but do not carry the font within themselves. There may, however, be some cases where you need to bundle a font with the document. Certain document formats may allow the inclusion of an unmodified font within their file structure which consists of a compressed folder containing the various resources forming the document (such as pictures and thumbnails). Including fonts within such a structure is understood as being different from embedding but rather similar to bundling (or mere aggregation) for which the licensing makes explicit provision. In this case the font is conveyed unchanged whereas embedding a font transforms it from the original format. The OFL does not allow anyone to extract the font from such a structure to then redistribute it under another license. The explicit permission to redistribute and embed does not cancel the requirement for the Font Software to remain under the license chosen by its Author(s).
-
-1.13 If OFL fonts are extracted from a document in which they are embedded (such as a PDF file), what can be done with them? Is this a risk to Author(s)?
-The few utilities that can extract fonts embedded in a PDF will only output limited amounts of outlines - not a complete font. To create a working font from this method is much more difficult than finding the source of the original OFL font. So there is little chance that an OFL font would be extracted and redistributed inappropriately through this method. Even so, copyright laws address any misrepresentation of authorship. All Font Software released under the OFL and marked as such by the Author(s) is intended to remain under this license regardless of the distribution method, and cannot be redistributed under any other license. We strongly discourage any font extraction - we recommend directly using the font sources instead - but if you extract font outlines from a document please be considerate, use your common sense and respect the work of the Author(s) and the licensing model.
-
-1.14 What about sharing OFL fonts with friends on a CD, DVD or USB stick?
-You are very welcome to share open fonts with friends, family and colleagues on such removable media. Please make sure that you share and share-alike as much as possible from what the Author(s) released and that you don't strip away useful information which may not be present in the binary font files themselves. Just remember that in the case where you sell the font, it has to come bundled with software.
-
-
-2 ABOUT MODIFYING OFL LICENSED FONTS
-
-2.1 Can I change the fonts? Are there any limitations to what things I can and cannot change?
-You are allowed to change anything, as long as such changes do not violate the terms of the license. In other words, you are not allowed to remove the copyright statement(s) from the font, but you could add additional information into it that covers your contribution.
-
-2.2 I have a font that needs a few extra glyphs - can I take them from an OFL licensed font and copy them into mine?
-Yes, but if you distribute that font to others it must be under the OFL, and include the information mentioned in condition 2 of the license.
-
-2.3 Can I charge people for my additional work? In other words, if I add a bunch of special glyphs and/or OpenType/Graphite code, can I sell the enhanced font?
-Not by itself. Derivative fonts must be released under the OFL and cannot be sold by themselves. It is permitted, however, to include them in a larger software package (such as text editors, office suites or operating systems), even if the larger package is sold. In that case, you are strongly encouraged, but not required, to also make that derived font easily and freely available outside of the larger package.
-
-2.4 Can I pay someone to enhance the fonts for my use and distribution?
-Yes. This is a good way to fund the further development of the fonts. Keep in mind, however, that if the font is distributed to others it must be under the OFL. You won't be able to recover your investment by exclusively selling the font, but you will be making a valuable contribution to the community. Please remember how you have benefitted from the contributions of others.
-
-2.5 I need to make substantial revisions to the font to make it work with my program. It will be a lot of work, and a big investment, and I want to be sure that it can only be distributed with my program. Can I restrict its use?
-No. If you redistribute a Modified Version of the font it must be under the OFL. You may not restrict it in any way. This is intended to ensure that all released improvements to the fonts become available to everyone. But you will likely get an edge over competitors by being the first to distribute a bundle with the enhancements. Again, please remember how you have benefitted from the contributions of others.
-
-2.6 Do I have to make any derivative fonts (including extended source files, build scripts, documentation, etc.) publicly available?
-No, but please do share your improvements with others. You may find that you receive more than what you gave in return.
-
-2.7 Why can't I use the Reserved Font Name(s) in my derivative font names? I'd like people to know where the design came from.
-The best way to acknowledge the source of the design is to thank the original authors and any other contributors in the files that are distributed with your revised font (although no acknowledgement is required). The FONTLOG is a natural place to do this. Reserved Font Name(s) ensure that the only fonts that have the original names are the unmodified Original Versions. This allows designers to maintain artistic integrity while allowing collaboration to happen. It eliminates potential confusion and name conflicts. When choosing a name be creative and avoid names that reuse almost all the same letters in the same order or sound like the original. Keep in mind that the Copyright Holder(s) can allow a specific trusted partner to use Reserved Font Name(s) through a separate written agreement.
-
-2.8 What do you mean by "primary name as presented to the user"? Are you referring to the font menu name?
-Yes, the requirement to change the visible name used to differentiate the font from others applies to the font menu name and other mechanisms to specify a font in a document. It would be fine, for example, to keep a text reference to the original fonts in the description field, in your modified source file or in documentation provided alongside your derivative as long as no one could be confused that your modified source is the original. But you cannot use the Reserved Font Names in any way to identify the font to the user (unless the Copyright Holder(s) allow(s) it through a separate agreement; see section 2.7). Users who install derivatives ("Modified Versions") on their systems should not see any of the original names ("Reserved Font Names") in their font menus, for example. Again, this is to ensure that users are not confused and do not mistake a font for another and so expect features only another derivative or the Original Version can actually offer. Ultimately, creating name conflicts will cause many problems for the users as well as for the designer of both the Original and Modified versions, so please think ahead and find a good name for your own derivative. Font substitution systems like fontconfig, or application-level font fallback configuration within OpenOffice.org or Scribus, will also get very confused if the name of the font they are configured to substitute to actually refers to another physical font on the user's hard drive. It will help everyone if Original Versions and Modified Versions can easily be distinguished from one another and from other derivatives. The substitution mechanism itself is outside the scope of the license. Users can always manually change a font reference in a document or set up some kind of substitution at a higher level but at the lower level the fonts themselves have to respect the Reserved Font Name(s) requirement to prevent ambiguity. If a substitution is currently active the user should be aware of it.
-
-2.9 Am I not allowed to use any part of the Reserved Font Names?
-You may not use the words of the font names, but you would be allowed to use parts of words, as long as you do not use any word from the Reserved Font Names entirely. We do not recommend using parts of words because of potential confusion, but it is allowed. For example, if "Foobar" was a Reserved Font Name, you would be allowed to use "Foo" or "bar", although we would not recommend it. Such an unfortunate choice would confuse the users of your fonts as well as make it harder for other designers to contribute.
-
-2.10 So what should I, as an author, identify as Reserved Font Names?
-Original authors are encouraged to name their fonts using clear, distinct names, and only declare the unique parts of the name as Reserved Font Names. For example, the author of a font called "Foobar Sans" would declare "Foobar" as a Reserved Font Name, but not "Sans", as that is a common typographical term, and may be a useful word to use in a derivative font name. Reserved Font Names should also be single words. A font called "Flowing River" should have Reserved Font Names "Flowing" and "River", not "Flowing River".
-
-2.11 Do I, as an author, have to identify any Reserved Font Names?
-No, but we strongly encourage you to do so. This is to avoid confusion between your work and Modified versions. You may, however, give certain trusted parties the right to use any of your Reserved Font Names through separate written agreements. For example, even if "Foobar" is a RFN, you could write up an agreement to give company "XYZ" the right to distribute a modified version with a name that includes "Foobar". This allows for freedom without confusion.
-
-2.12 Are any names (such as the main font name) reserved by default?
-No. That is a change to the license as of version 1.1. If you want any names to be Reserved Font Names, they must be specified after the copyright statement(s).
-
-2.13 What is this FONTLOG thing exactly?
-It has three purposes: 1) to provide basic information on the font to users and other developers, 2) to document changes that have been made to the font or accompanying files, either by the original authors or others, and 3) to provide a place to acknowledge the authors and other contributors. Please use it! See below for details on how changes should be noted.
-
-2.14 Am I required to update the FONTLOG?
-No, but users, designers and other developers might get very frustrated at you if you don't! People need to know how derivative fonts differ from the original, and how to take advantage of the changes, or build on them.
-
-
-3 ABOUT THE FONTLOG
-
-The FONTLOG can take a variety of formats, but should include these four sections:
-
-3.1 FONTLOG for <FontFamilyName>
-This file provides detailed information on the <FontFamilyName> Font Software. This information should be distributed along with the <FontFamilyName> fonts and any derivative works.
-
-3.2 Basic Font Information
-(Here is where you would describe the purpose and brief specifications for the font project, and where users can find more detailed documentation. It can also include references to how changes can be contributed back to the Original Version. You may also wish to include a short guide to the design, or a reference to such a document.)
-
-3.3 ChangeLog
-(This should list both major and minor changes, most recent first. Here are some examples:)
-
-7 February 2007 (Pat Johnson) <NewFontFamilyName> Version 1.3
-- Added Greek and Cyrillic glyphs
-- Released as "<NewFontFamilyName>"
-
-7 March 2006 (Fred Foobar) <NewFontFamilyName> Version 1.2
-- Tweaked contextual behaviours
-- Released as "<NewFontFamilyName>"
-
-1 Feb 2005 (Jane Doe) <NewFontFamilyName> Version 1.1
-- Improved build script performance and verbosity
-- Extended the smart code documentation
-- Corrected minor typos in the documentation
-- Fixed position of combining inverted breve below (U+032F)
-- Added OpenType/Graphite smart code for Armenian
-- Added Armenian glyphs (U+0531 -> U+0587)
-- Released as "<NewFontFamilyName>"
-
-1 Jan 2005 (Joe Smith) <FontFamilyName> Version 1.0
-- Initial release of font "<FontFamilyName>"
-
-3.4 Acknowledgements
-(Here is where contributors can be acknowledged.
-
-If you make modifications be sure to add your name (N), email (E), web-address (W) and description (D). This list is sorted by last name in alphabetical order.)
-
-N: Jane Doe
-E: jane@university.edu
-W: http://art.university.edu/projects/fonts
-D: Contributor - Armenian glyphs and code
-
-N: Fred Foobar
-E: fred@foobar.org
-W: http://foobar.org
-D: Contributor - misc Graphite fixes
-
-N: Pat Johnson
-E: pat@fontstudio.org
-W: http://pat.fontstudio.org
-D: Designer - Greek & Cyrillic glyphs based on Roman design
-
-N: Tom Parker
-E: tom@company.com
-W: http://www.company.com/tom/projects/fonts
-D: Engineer - original smart font code
-
-N: Joe Smith
-E: joe@fontstudio.org
-W: http://joe.fontstudio.org
-D: Designer - original Roman glyphs
-
-(Original authors can also include information here about their organization.)
-
-
-4 ABOUT MAKING CONTRIBUTIONS
-
-4.1 Why should I contribute my changes back to the original authors?
-It would benefit many people if you contributed back to what you've received. Providing your contributions and improvements to the fonts and other components (data files, source code, build scripts, documentation, etc.) could be a tremendous help and would encourage others to contribute as well and 'give back', which means you will have an opportunity to benefit from other people's contributions as well. Sometimes maintaining your own separate version takes more effort than merging back with the original. Be aware that any contributions, however, must be either your own original creation or work that you own, and you may be asked to affirm that clearly when you contribute.
-
-4.2 I've made some very nice improvements to the font, will you consider adopting them and putting them into future Original Versions?
-Most authors would be very happy to receive such contributions. Keep in mind that it is unlikely that they would want to incorporate major changes that would require additional work on their end. Any contributions would likely need to be made for all the fonts in a family and match the overall design and style. Authors are encouraged to include a guide to the design with the fonts. It would also help to have contributions submitted as patches or clearly marked changes (the use of smart source revision control systems like subversion, svk, mercurial, git or bzr is a good idea). Examples of useful contributions are bug fixes, additional glyphs, stylistic alternates (and the smart font code to access them) or improved hinting.
-
-4.3 How can I financially support the development of OFL fonts?
-It is likely that most authors of OFL fonts would accept financial contributions - contact them for instructions on how to do this. Such contributions would support future development. You can also pay for others to enhance the fonts and contribute the results back to the original authors for inclusion in the Original Version.
-
-(In case of the font, that comes with this copy of the FAQ-file, you will find a paypal(tm) donation link on the fonts-page of www.peter-wiegel.de)
-
-
-5 ABOUT THE LICENSE
-
-5.1 I see that this is version 1.1 of the license. Will there be later changes?
-Version 1.1 is the first minor revision of the OFL. We are confident that version 1.1 will meet most needs, but are open to future improvements. Any revisions would be for future font releases, and previously existing licenses would remain in effect. No retroactive changes are possible, although the Copyright Holder(s) can re-release the font under a revised OFL. All versions will be available on our web site: http://scripts.sil.org/OFL.
-
-5.2 Can I use the SIL Open Font License for my own fonts?
-Yes! We heartily encourage anyone to use the OFL to distribute their own original fonts. It is a carefully constructed license that allows great freedom along with enough artistic integrity protection for the work of the authors as well as clear rules for other contributors and those who redistribute the fonts. Some additional information about using the OFL is included at the end of this FAQ.
-
-5.3 Does this license restrict the rights of the Copyright Holder(s)?
-No. The Copyright Holder(s) still retain(s) all the rights to their creation; they are only releasing a portion of it for use in a specific way. For example, the Copyright Holder(s) may choose to release a 'basic' version of their font under the OFL, but sell a restricted 'enhanced' version. Only the Copyright Holder(s) can do this.
-
-5.4 Is the OFL a contract or a license?
-The OFL is a license and not a contract and so does not require you to sign it to have legal validity. By using, modifying and redistributing components under the OFL you indicate that you accept the license.
-
-5.5 How about translating the license and the FAQ into other languages?
-SIL certainly recognises the need for people who are not familiar with English to be able to understand the OFL and this FAQ better in their own language. Making the license very clear and readable is a key goal of the OFL.
-
-If you are an experienced translator, you are very welcome to help by translating the OFL and its FAQ so that designers and users in your language community can understand the license better. But only the original English version of the license has legal value and has been approved by the community. Translations do not count as legal substitutes and should only serve as a way to explain the original license. SIL - as the author and steward of the license for the community at large - does not approve any translation of the OFL as legally valid because even small translation ambiguities could be abused and create problems.
-
-We give permission to publish unofficial translations into other languages provided that they comply with the following guidelines:
-
-- put the following disclaimer in both English and the target language stating clearly that the translation is unofficial:
-
-"This is an unofficial translation of the SIL Open Font License into $language. It was not published by SIL International, and does not legally state the distribution terms for fonts that use the OFL. A release under the OFL is only valid when using the original English text.
-
-However, we recognize that this unofficial translation will help users and designers not familiar with English to understand the SIL OFL better and make it easier to use and release font families under this collaborative font design model. We encourage designers who consider releasing their creation under the OFL to read the FAQ in their own language if it is available.
-
-Please go to http://scripts.sil.org/OFL for the official version of the license and the accompanying FAQ."
-
-- keep your unofficial translation current and update it at our request if needed, for example if there is any ambiguity which could lead to confusion.
-
-If you start such a unofficial translation effort of the OFL and its accompanying FAQ please let us know, thank you.
-
-
-6 ABOUT SIL INTERNATIONAL
-
-6.1 Who is SIL International and what does it do?
-SIL International is a worldwide faith-based education and development organization (NGO) that studies, documents, and assists in developing the world's lesser-known languages through literacy, linguistics, translation, and other academic disciplines. SIL makes its services available to all without regard to religious belief, political ideology, gender, race, or ethnic background. SIL's members and volunteers share a Christian commitment.
-
-6.2 What does this have to do with font licensing?
-The ability to read, write, type and publish in one's own language is one of the most critical needs for millions of people around the world. This requires fonts that are widely available and support lesser-known languages. SIL develops - and encourages others to develop - a complete stack of writing systems implementation components available under open licenses. This open stack includes input methods, smart fonts, smart rendering libraries and smart applications. There has been a need for a common open license that is specifically applicable to fonts and related software (a crucial component of this stack) so SIL developed the SIL Open Font License with the help of the FLOSS community.
-
-6.3 How can I contact SIL?
-Our main web site is: http://www.sil.org/
-Our site about complex scripts is: http://scripts.sil.org/
-Information about this license (including contact email information) is at: http://scripts.sil.org/OFL
-
-
-7 ABOUT USING THE OFL FOR YOUR ORIGINAL FONTS
-
-If you want to release your fonts under the OFL, you only need to do the following:
-
-7.1 Put your copyright and reserved font names information in the beginning of the main OFL file (simply use the dedicated placeholders).
-7.2 Put your copyright and the OFL references in your various font files (such as in the copyright, license and description fields) and in your other components (build scripts, glyph databases, documentation, rendering samples, etc). Accurate metadata in your font files is beneficial to you as an increasing number of applications are exposing this information to the user. For example, clickable links can bring users back to your website and let them know about other work you have done or services you provide. Depending on the format of your fonts and sources, you can use template human-readable headers or machine-readable metadata.
-7.3 Write an initial FONTLOG for your font and include it in the release package.
-7.4 Include the OFL license file in your release package.
-7.5 We also highly recommend you include the relevant practical documentation on the license by putting the OFL-FAQ in your package.
-7.6 If you wish, you can use the OFL Graphics on your web page.
-
-That's all. If you have any more questions please get in touch with us.
-
-
diff --git a/themes/conspiracy/assets/fonts/manuskript_gothisch/Open Font License.txt b/themes/conspiracy/assets/fonts/manuskript_gothisch/Open Font License.txt deleted file mode 100644 index 71d47ee..0000000 --- a/themes/conspiracy/assets/fonts/manuskript_gothisch/Open Font License.txt +++ /dev/null @@ -1,95 +0,0 @@ -Copyright (c) 2013, Peter Wiegel, www.peter-wiegel.de, wiegel@peter-wiegel.de
-with Reserved Font Name Manuskript Gothisch
-
-
-This Font Software is licensed under the SIL Open Font License, Version 1.1.
-This license is copied below, and is also available with a FAQ at:
-http://scripts.sil.org/OFL
-
-
------------------------------------------------------------
-SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
------------------------------------------------------------
-
-PREAMBLE
-The goals of the Open Font License (OFL) are to stimulate worldwide
-development of collaborative font projects, to support the font creation
-efforts of academic and linguistic communities, and to provide a free and
-open framework in which fonts may be shared and improved in partnership
-with others.
-
-The OFL allows the licensed fonts to be used, studied, modified and
-redistributed freely as long as they are not sold by themselves. The
-fonts, including any derivative works, can be bundled, embedded,
-redistributed and/or sold with any software provided that any reserved
-names are not used by derivative works. The fonts and derivatives,
-however, cannot be released under any other type of license. The
-requirement for fonts to remain under this license does not apply
-to any document created using the fonts or their derivatives.
-
-DEFINITIONS
-"Font Software" refers to the set of files released by the Copyright
-Holder(s) under this license and clearly marked as such. This may
-include source files, build scripts and documentation.
-
-"Reserved Font Name" refers to any names specified as such after the
-copyright statement(s).
-
-"Original Version" refers to the collection of Font Software components as
-distributed by the Copyright Holder(s).
-
-"Modified Version" refers to any derivative made by adding to, deleting,
-or substituting -- in part or in whole -- any of the components of the
-Original Version, by changing formats or by porting the Font Software to a
-new environment.
-
-"Author" refers to any designer, engineer, programmer, technical
-writer or other person who contributed to the Font Software.
-
-PERMISSION & CONDITIONS
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of the Font Software, to use, study, copy, merge, embed, modify,
-redistribute, and sell modified and unmodified copies of the Font
-Software, subject to the following conditions:
-
-1) Neither the Font Software nor any of its individual components,
-in Original or Modified Versions, may be sold by itself.
-
-2) Original or Modified Versions of the Font Software may be bundled,
-redistributed and/or sold with any software, provided that each copy
-contains the above copyright notice and this license. These can be
-included either as stand-alone text files, human-readable headers or
-in the appropriate machine-readable metadata fields within text or
-binary files as long as those fields can be easily viewed by the user.
-
-3) No Modified Version of the Font Software may use the Reserved Font
-Name(s) unless explicit written permission is granted by the corresponding
-Copyright Holder. This restriction only applies to the primary font name as
-presented to the users.
-
-4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
-Software shall not be used to promote, endorse or advertise any
-Modified Version, except to acknowledge the contribution(s) of the
-Copyright Holder(s) and the Author(s) or with their explicit written
-permission.
-
-5) The Font Software, modified or unmodified, in part or in whole,
-must be distributed entirely under this license, and must not be
-distributed under any other license. The requirement for fonts to
-remain under this license does not apply to any document created
-using the Font Software.
-
-TERMINATION
-This license becomes null and void if any of the above conditions are
-not met.
-
-DISCLAIMER
-THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
-OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
-COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
-DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
-OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/themes/conspiracy/assets/fonts/manuskript_gothisch/download.txt b/themes/conspiracy/assets/fonts/manuskript_gothisch/download.txt deleted file mode 100644 index 0fa3517..0000000 --- a/themes/conspiracy/assets/fonts/manuskript_gothisch/download.txt +++ /dev/null @@ -1,2 +0,0 @@ -Downloaded from: https://www.dafont.com/manuskript-gothisch.font -License included in download: OFL diff --git a/themes/conspiracy/assets/fonts/nyght-serif-main/COPYRIGHT.md b/themes/conspiracy/assets/fonts/nyght-serif-main/COPYRIGHT.md deleted file mode 100644 index d8d9a7c..0000000 --- a/themes/conspiracy/assets/fonts/nyght-serif-main/COPYRIGHT.md +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2023, Maksym Kobuzan <maxkobuzan@gmail.com> diff --git a/themes/conspiracy/assets/fonts/nyght-serif-main/HELLO.txt b/themes/conspiracy/assets/fonts/nyght-serif-main/HELLO.txt deleted file mode 100644 index 36f15ee..0000000 --- a/themes/conspiracy/assets/fonts/nyght-serif-main/HELLO.txt +++ /dev/null @@ -1,35 +0,0 @@ -ENG - - -Hi! - -Thank you for your interest! I put a lot of my time and effort into my fonts. -And I would be very interested to see how you use the NYGHT SERIF and other my typefaces. - -Tag me @mkobuzan or send it to maxkobuzan@gmail.com your Font In Use. I would even repost some of this to my Instagram. - -Enjoy! - -Glory to Ukraine, - -Maksym. - - - - -UKR - - -Привіт! - -Дякую за твоє зацікавлення! Я вкладаю багато часу та зусиль у свої шрифти. -І мені було б дуже цікаво подивитися, як ти використовуєш NYGHT SERIF і інші мої шрифти. - -Познач мене @mkobuzan або надішли на maxkobuzan@gmail.com свій проєкт, в якому використано мій шрифт. -Щось із цього я б репостнув би собі в Instagram. - -Насолоджуйся! - -Слава Україні, - -Максим. diff --git a/themes/conspiracy/assets/fonts/nyght-serif-main/LICENSE.txt b/themes/conspiracy/assets/fonts/nyght-serif-main/LICENSE.txt deleted file mode 100644 index 3182580..0000000 --- a/themes/conspiracy/assets/fonts/nyght-serif-main/LICENSE.txt +++ /dev/null @@ -1,94 +0,0 @@ -Copyright (c) 2022, Maksym Kobuzan (maxkobuzan@gmail.com), -with Reserved Font Name NYGHT SERIF. - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -http://scripts.sil.org/OFL - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/themes/conspiracy/assets/fonts/nyght-serif-main/README.md b/themes/conspiracy/assets/fonts/nyght-serif-main/README.md deleted file mode 100644 index d8ca75c..0000000 --- a/themes/conspiracy/assets/fonts/nyght-serif-main/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# NYGHT SERIF v0.5 - -NYGHT SERIF (version 0.5) is a contemporary serif designed by Maksym Kobuzan (www.behance.net/mkobuzan). - -## Description - -NYGHT SERIF is a contemporary serif with a spicy character. Its contrasting forms combine smooth curves and sharp as a blade serifs and spurs. NYGHT SERIF also knows Extended Latin. It also knows Cyrillic. But only the Ukrainian alphabet. NYGHT SERIF is absolutely FREE for personal and commercial use. It also has an Open Font License (OFL). NYGHT SERIF will be published in progress. And there will be many updates. - -## Current Version - -FAMILY UPDATE 0.5 - -1. 6 new styles. Namely Regular, Medium, Bold. And also Italics, of course. Now it's a full-fledged Type Family! 💚 -2. Added support for Bulgarian language. Namely, the letters of the Bulgarian Cyrillic alphabet! -3. Added some basic ligatures like ff tt ft. -4. Balanced the width of some Сapital Letters in Light weight. Some letters have become wider, for example P R B. And some letters have become, on the contrary, a little narrower. -5. And as usual, multiple minor changes in the curves. The curves are smoother and more uniform. Some bugs have also been fixed. 🤌 - -So you need to update all styles as well. And thanks for downloading and enjoying! - -## Visuals - -![specimen1](documentation/images/NyghtSerif01.jpg) -![specimen1](documentation/images/NyghtSerif02.jpg) -![specimen1](documentation/images/NyghtSerif11.jpg) - -## License -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is available [here](http://www.tunera.xyz/licenses/sil-open-font-license-1.1/) - -## Repository Layout -This font repository structure is inspired by [Unified Font Repository v0.3](https://github.com/unified-font-repository/Unified-Font-Repository). diff --git a/themes/conspiracy/assets/fonts/nyght-serif-main/TRADEMARKS.md b/themes/conspiracy/assets/fonts/nyght-serif-main/TRADEMARKS.md deleted file mode 100644 index 74072a7..0000000 --- a/themes/conspiracy/assets/fonts/nyght-serif-main/TRADEMARKS.md +++ /dev/null @@ -1 +0,0 @@ -NYGHT SERIF is a trademark of Maksym Kobuzan (2023). diff --git a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/.gitkeep b/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/.gitkeep deleted file mode 100644 index e69de29..0000000 --- a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/.gitkeep +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/.gitkeep b/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/.gitkeep deleted file mode 100644 index e69de29..0000000 --- a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/.gitkeep +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/roboto_slab/LICENSE.txt b/themes/conspiracy/assets/fonts/roboto_slab/LICENSE.txt deleted file mode 100644 index 75b5248..0000000 --- a/themes/conspiracy/assets/fonts/roboto_slab/LICENSE.txt +++ /dev/null @@ -1,202 +0,0 @@ -
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- 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.
diff --git a/themes/conspiracy/assets/fonts/roboto_slab/README.txt b/themes/conspiracy/assets/fonts/roboto_slab/README.txt deleted file mode 100644 index 091e21f..0000000 --- a/themes/conspiracy/assets/fonts/roboto_slab/README.txt +++ /dev/null @@ -1,71 +0,0 @@ -Roboto Slab Variable Font -========================= - -This download contains Roboto Slab as both a variable font and static fonts. - -Roboto Slab is a variable font with this axis: - wght - -This means all the styles are contained in a single file: - RobotoSlab-VariableFont_wght.ttf - -If your app fully supports variable fonts, you can now pick intermediate styles -that aren’t available as static fonts. Not all apps support variable fonts, and -in those cases you can use the static font files for Roboto Slab: - static/RobotoSlab-Thin.ttf - static/RobotoSlab-ExtraLight.ttf - static/RobotoSlab-Light.ttf - static/RobotoSlab-Regular.ttf - static/RobotoSlab-Medium.ttf - static/RobotoSlab-SemiBold.ttf - static/RobotoSlab-Bold.ttf - static/RobotoSlab-ExtraBold.ttf - static/RobotoSlab-Black.ttf - -Get started ------------ - -1. Install the font files you want to use - -2. Use your app's font picker to view the font family and all the -available styles - -Learn more about variable fonts -------------------------------- - - https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts - https://variablefonts.typenetwork.com - https://medium.com/variable-fonts - -In desktop apps - - https://theblog.adobe.com/can-variable-fonts-illustrator-cc - https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts - -Online - - https://developers.google.com/fonts/docs/getting_started - https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide - https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts - -Installing fonts - - MacOS: https://support.apple.com/en-us/HT201749 - Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux - Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows - -Android Apps - - https://developers.google.com/fonts/docs/android - https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts - -License -------- -Please read the full license text (LICENSE.txt) to understand the permissions, -restrictions and requirements for usage, redistribution, and modification. - -You can use them in your products & projects – print or digital, -commercial or otherwise. - -This isn't legal advice, please consider consulting a lawyer and see the full -license for all details. diff --git a/themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-Black.ttf b/themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-Black.ttf Binary files differdeleted file mode 100644 index 0c57d86..0000000 --- a/themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-Black.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-Bold.ttf b/themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-Bold.ttf Binary files differdeleted file mode 100644 index 596898c..0000000 --- a/themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-Bold.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-ExtraBold.ttf b/themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-ExtraBold.ttf Binary files differdeleted file mode 100644 index e8dd4f9..0000000 --- a/themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-ExtraBold.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-ExtraLight.ttf b/themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-ExtraLight.ttf Binary files differdeleted file mode 100644 index b152888..0000000 --- a/themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-ExtraLight.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-Light.ttf b/themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-Light.ttf Binary files differdeleted file mode 100644 index 1c204ae..0000000 --- a/themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-Light.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-Medium.ttf b/themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-Medium.ttf Binary files differdeleted file mode 100644 index 50657b8..0000000 --- a/themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-Medium.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-Regular.ttf b/themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-Regular.ttf Binary files differdeleted file mode 100644 index cf612b8..0000000 --- a/themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-Regular.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-SemiBold.ttf b/themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-SemiBold.ttf Binary files differdeleted file mode 100644 index 644abc4..0000000 --- a/themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-SemiBold.ttf +++ /dev/null diff --git a/themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-Thin.ttf b/themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-Thin.ttf Binary files differdeleted file mode 100644 index 3e7d3d3..0000000 --- a/themes/conspiracy/assets/fonts/roboto_slab/static/RobotoSlab-Thin.ttf +++ /dev/null diff --git a/themes/conspiracy/layouts/404.html b/themes/conspiracy/layouts/404.html deleted file mode 100644 index e69de29..0000000 --- a/themes/conspiracy/layouts/404.html +++ /dev/null diff --git a/themes/conspiracy/layouts/_default/baseof.html b/themes/conspiracy/layouts/_default/baseof.html deleted file mode 100644 index eb163b6..0000000 --- a/themes/conspiracy/layouts/_default/baseof.html +++ /dev/null @@ -1,17 +0,0 @@ -<!DOCTYPE html> -<html> - {{- partial "head.html" . -}} - <body> - {{- partial "header.html" . -}} - {{- block "main" . }}{{- end }} - {{- partial "footer.html" . -}} - <script> - if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser - contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to - prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition. - In support of an open web, this website does not function with this DRM. Please install a browser such - as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports - ad blockers.`; - </script> - </body> -</html> diff --git a/themes/conspiracy/layouts/_default/list.html b/themes/conspiracy/layouts/_default/list.html deleted file mode 100644 index 02703b4..0000000 --- a/themes/conspiracy/layouts/_default/list.html +++ /dev/null @@ -1,18 +0,0 @@ -{{- define "main" }} - {{- if not .IsHome | and .Title }} - <header> - <h1>{{ .Title }}</h1> - {{- partial "breadcrumbs.html" . }} - </header> - {{- end }} - <main class="cards"> - {{- if .Content }} - <div class="intro"> - {{ .Content }} - </div> - {{- end }} - {{- range .Pages }} - {{ partial "card.html" . }} - {{- end }} - </main> -{{- end }} diff --git a/themes/conspiracy/layouts/_default/single.html b/themes/conspiracy/layouts/_default/single.html deleted file mode 100644 index 231daf7..0000000 --- a/themes/conspiracy/layouts/_default/single.html +++ /dev/null @@ -1,19 +0,0 @@ -{{- define "main" }} - {{- if not .IsHome | and .Title }} - <header> - <h1>{{ .Title }}</h1> - {{- partial "breadcrumbs.html" . }} - {{- if .Params.Date }} <strong>{{ .Date.Format "2006-01-02" }}</strong>{{- end }} - </header> - {{- end }} - <main> - {{- if .Params.external_links }} - <div class="links"> - {{- range .Params.external_links }} - <a href="{{ .url | absURL }}">{{ .name }}</a> - {{- end }} - </div> - {{- end }} - {{ .Content }} - </main> -{{- end }} diff --git a/themes/conspiracy/layouts/index.html b/themes/conspiracy/layouts/index.html deleted file mode 100644 index 808ddc8..0000000 --- a/themes/conspiracy/layouts/index.html +++ /dev/null @@ -1,25 +0,0 @@ -{{- define "main" }} - <header> - <h1>{{ .Title }}</h1> - </header> - <main class="cards"> - {{- if .Content }} - <div class="intro"> - {{ .Content }} - </div> - {{- end }} - - {{- range $config := site.Params.homepage_categories }} - <h2>{{ .title }}</h2> - {{- range $entry := first $config.count (where site.RegularPages "Section" "==" $config.key) }} - {{ partial "card.html" $entry }} - {{- end }} - - <div class="pagination-links"> - {{- with site.GetPage (printf "/%s" .key) }} - <a href="{{ .RelPermalink }}">See more<span class="arrow-right"></span></a> - {{- end }} - </div> - {{- end }} - </main> -{{- end }} diff --git a/themes/conspiracy/layouts/partials/breadcrumbs.html b/themes/conspiracy/layouts/partials/breadcrumbs.html deleted file mode 100644 index ff82e73..0000000 --- a/themes/conspiracy/layouts/partials/breadcrumbs.html +++ /dev/null @@ -1,7 +0,0 @@ -{{/* https://github.com/adityatelange/hugo-PaperMod/blob/master/layouts/partials/breadcrumbs.html */}} -<ul class="breadcrumbs"> - {{- range .Ancestors.Reverse }} - <li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li> - {{- end -}} - <li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li> -</ul> diff --git a/themes/conspiracy/layouts/partials/card.html b/themes/conspiracy/layouts/partials/card.html deleted file mode 100644 index 21eeebf..0000000 --- a/themes/conspiracy/layouts/partials/card.html +++ /dev/null @@ -1,17 +0,0 @@ -<div class="card"> - {{- if .Title }}<h3><a href="{{ .RelPermalink }}">{{ .Title }}</a></h3>{{- end }} - {{- if and .Date (not (isset (index .Ancestors 0).Params "hide_date")) }}<strong>{{ .Date.Format "2006-01-02" }}</strong>{{- end }} - - <div class="summary"> - {{ .Summary | safeHTML }} - <a href="{{ .Permalink }}">Read more</a> - </div> - - {{- if .Params.external_links }} - <div class="links"> - {{- range .Params.external_links }} - <a href="{{ .url | absURL }}">{{ .name }}</a> - {{- end }} - </div> - {{- end }} -</div> diff --git a/themes/conspiracy/layouts/partials/footer.html b/themes/conspiracy/layouts/partials/footer.html deleted file mode 100644 index adf7aee..0000000 --- a/themes/conspiracy/layouts/partials/footer.html +++ /dev/null @@ -1,6 +0,0 @@ -<footer> - Copyright © {{ now.Year }} {{ site.Copyright }} - {{- range site.Params.footer_links }} - / <a href="{{.url | relURL }}">{{ .name }}</a> - {{- end }} -</footer> diff --git a/themes/conspiracy/layouts/partials/head.html b/themes/conspiracy/layouts/partials/head.html deleted file mode 100644 index 1196d00..0000000 --- a/themes/conspiracy/layouts/partials/head.html +++ /dev/null @@ -1,10 +0,0 @@ -<head> - <meta charset="utf-8"> - <title>{{ if .IsHome }}{{ else }}{{ if .Title }}{{ .Title }} | {{ end }}{{ end }}{{ site.Title }}</title> - <meta name="description" content=""> - <meta name="viewport" content="width=device-width, initial-scale=1"> - <meta name="mobile-web-app-capable" content="yes"> - <meta name="color-scheme" content="dark light"> - {{- $stylesheet := resources.Get "css/style.css" | resources.ExecuteAsTemplate "style.css" . }} - <link rel="stylesheet" href="{{ $stylesheet.RelPermalink }}"> -</head> diff --git a/themes/conspiracy/layouts/partials/header.html b/themes/conspiracy/layouts/partials/header.html deleted file mode 100644 index e252f1f..0000000 --- a/themes/conspiracy/layouts/partials/header.html +++ /dev/null @@ -1,20 +0,0 @@ -<nav> - <div class="internal"> - {{- $currentPage := . }} - <!-- todo: put this in a "pages" drop down on mobile --> - {{- range site.Menus.main }} - {{- $menu_item_url := .URL | absURL }} - {{- $page_url:= $currentPage.Permalink | absURL }} - <a href="{{ .URL }}" title="{{ .Title | default .Name }}" {{- if eq $menu_item_url $page_url }} class="active" {{- end }}> - {{- .Pre }} - {{- .Name -}} - {{ .Post -}} - </a> - {{- end }} - </div> - <div class="external"> - {{- range site.Params.profile_links }} - <a href="{{ .url | absURL }}" title="{{ .name }}">{{ .name }}</a> - {{- end }} - </span> -</nav> diff --git a/themes/conspiracy/theme.toml b/themes/conspiracy/theme.toml deleted file mode 100644 index dbae125..0000000 --- a/themes/conspiracy/theme.toml +++ /dev/null @@ -1,21 +0,0 @@ -# theme.toml template for a Hugo theme -# See https://github.com/gohugoio/hugoThemes#themetoml for an example - -name = "Conspiracy" -license = "MIT" -licenselink = "https://github.com/yourname/yourtheme/blob/master/LICENSE" -description = "" -homepage = "http://example.com/" -tags = [] -features = [] -min_version = "0.41.0" - -[author] - name = "" - homepage = "" - -# If porting an existing theme -[original] - name = "" - homepage = "" - repo = "" |