From 71b5f84a6f189743954752018464320a5fffa072 Mon Sep 17 00:00:00 2001 From: Thomas Hooge Date: Tue, 24 Dec 2024 09:36:04 +0100 Subject: [PATCH] =?UTF-8?q?Erstver=C3=B6ffentlichung=20Weihnachten=202024?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + INSTALL | 13 + README | 61 ++++ fonts/AtariST8x16.ttf | Bin 0 -> 34688 bytes fonts/DSEG7Classic-BoldItalic.ttf | Bin 0 -> 23464 bytes fonts/Ubuntu-B.ttf | Bin 0 -> 333616 bytes images/Falling.png | Bin 0 -> 1142 bytes images/Falling_Fast.png | Bin 0 -> 626 bytes images/Falling_Very_Fast.png | Bin 0 -> 841 bytes images/Rising.png | Bin 0 -> 1044 bytes images/Rising_Fast.png | Bin 0 -> 569 bytes images/Rising_Very_Fast.png | Bin 0 -> 847 bytes images/Stationary.png | Bin 0 -> 559 bytes images/alarm.png | Bin 0 -> 150 bytes images/anchor.png | Bin 0 -> 148 bytes images/arrow_l1.png | Bin 0 -> 136 bytes images/arrow_r1.png | Bin 0 -> 135 bytes images/bee.png | Bin 0 -> 157 bytes images/buzzer.png | Bin 0 -> 124 bytes images/fuelcan.png | Bin 0 -> 4750 bytes images/fuelpump.png | Bin 0 -> 4256 bytes images/lightoff.png | Bin 0 -> 140 bytes images/lighton.png | Bin 0 -> 144 bytes images/lock.png | Bin 0 -> 143 bytes images/ship.png | Bin 0 -> 146 bytes images/swipe.png | Bin 0 -> 143 bytes images/water.png | Bin 0 -> 5354 bytes images/wmo0.png | Bin 0 -> 543 bytes images/wmo1.png | Bin 0 -> 518 bytes images/wmo2.png | Bin 0 -> 510 bytes images/wmo3.png | Bin 0 -> 592 bytes images/wmo4.png | Bin 0 -> 236 bytes images/wmo5.png | Bin 0 -> 584 bytes images/wmo6.png | Bin 0 -> 516 bytes images/wmo7.png | Bin 0 -> 507 bytes images/wmo8.png | Bin 0 -> 569 bytes nmea2000/__init__.py | 3 + nmea2000/boatdata.py | 567 ++++++++++++++++++++++++++++++ nmea2000/device.py | 63 ++++ nmea2000/devicelist.py | 17 + nmea2000/hbuffer.py | 300 ++++++++++++++++ nmea2000/lookup.py | 563 +++++++++++++++++++++++++++++ nmea2000/mavg.py | 25 ++ nmea2000/parser.py | 224 ++++++++++++ nmea2000/pgntype.py | 275 +++++++++++++++ nmea2000/queue.py | 27 ++ nmea2000/receiver.py | 61 ++++ nmea2000/routing.py | 17 + nmea2000/valdesc.py | 179 ++++++++++ obp60.conf | 96 +++++ obp60.py | 543 ++++++++++++++++++++++++++++ obp60.svg | 106 ++++++ pages/__init__.py | 36 ++ pages/anchor.py | 52 +++ pages/apparentwind.py | 16 + pages/autobahn.py | 102 ++++++ pages/barograph.py | 366 +++++++++++++++++++ pages/battery.py | 66 ++++ pages/battery2.py | 90 +++++ pages/bme280.py | 55 +++ pages/clock.py | 188 ++++++++++ pages/dst810.py | 40 +++ pages/exhaust.py | 58 +++ pages/fluid.py | 137 ++++++++ pages/fourvalues.py | 75 ++++ pages/fourvalues2.py | 77 ++++ pages/generator.py | 44 +++ pages/keel.py | 114 ++++++ pages/onegraph.py | 26 ++ pages/onevalue.py | 31 ++ pages/page.py | 202 +++++++++++ pages/rollpitch.py | 13 + pages/rudder.py | 103 ++++++ pages/skyview.py | 85 +++++ pages/solar.py | 40 +++ pages/system.py | 107 ++++++ pages/threevalues.py | 47 +++ pages/twographs.py | 14 + pages/twovalues.py | 67 ++++ pages/voltage.py | 216 ++++++++++++ pages/windrose.py | 119 +++++++ 81 files changed, 5728 insertions(+) create mode 100644 .gitignore create mode 100644 INSTALL create mode 100644 README create mode 100644 fonts/AtariST8x16.ttf create mode 100644 fonts/DSEG7Classic-BoldItalic.ttf create mode 100644 fonts/Ubuntu-B.ttf create mode 100644 images/Falling.png create mode 100644 images/Falling_Fast.png create mode 100644 images/Falling_Very_Fast.png create mode 100644 images/Rising.png create mode 100644 images/Rising_Fast.png create mode 100644 images/Rising_Very_Fast.png create mode 100644 images/Stationary.png create mode 100644 images/alarm.png create mode 100644 images/anchor.png create mode 100644 images/arrow_l1.png create mode 100644 images/arrow_r1.png create mode 100644 images/bee.png create mode 100644 images/buzzer.png create mode 100644 images/fuelcan.png create mode 100644 images/fuelpump.png create mode 100644 images/lightoff.png create mode 100644 images/lighton.png create mode 100644 images/lock.png create mode 100644 images/ship.png create mode 100644 images/swipe.png create mode 100644 images/water.png create mode 100644 images/wmo0.png create mode 100644 images/wmo1.png create mode 100644 images/wmo2.png create mode 100644 images/wmo3.png create mode 100644 images/wmo4.png create mode 100644 images/wmo5.png create mode 100644 images/wmo6.png create mode 100644 images/wmo7.png create mode 100644 images/wmo8.png create mode 100644 nmea2000/__init__.py create mode 100644 nmea2000/boatdata.py create mode 100644 nmea2000/device.py create mode 100644 nmea2000/devicelist.py create mode 100644 nmea2000/hbuffer.py create mode 100644 nmea2000/lookup.py create mode 100644 nmea2000/mavg.py create mode 100644 nmea2000/parser.py create mode 100644 nmea2000/pgntype.py create mode 100644 nmea2000/queue.py create mode 100644 nmea2000/receiver.py create mode 100644 nmea2000/routing.py create mode 100644 nmea2000/valdesc.py create mode 100644 obp60.conf create mode 100755 obp60.py create mode 100644 obp60.svg create mode 100644 pages/__init__.py create mode 100644 pages/anchor.py create mode 100644 pages/apparentwind.py create mode 100644 pages/autobahn.py create mode 100644 pages/barograph.py create mode 100644 pages/battery.py create mode 100644 pages/battery2.py create mode 100644 pages/bme280.py create mode 100644 pages/clock.py create mode 100644 pages/dst810.py create mode 100644 pages/exhaust.py create mode 100644 pages/fluid.py create mode 100644 pages/fourvalues.py create mode 100644 pages/fourvalues2.py create mode 100644 pages/generator.py create mode 100644 pages/keel.py create mode 100644 pages/onegraph.py create mode 100644 pages/onevalue.py create mode 100644 pages/page.py create mode 100644 pages/rollpitch.py create mode 100644 pages/rudder.py create mode 100644 pages/skyview.py create mode 100644 pages/solar.py create mode 100644 pages/system.py create mode 100644 pages/threevalues.py create mode 100644 pages/twographs.py create mode 100644 pages/twovalues.py create mode 100644 pages/voltage.py create mode 100644 pages/windrose.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9b5e2e8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*~ +__pycache__ diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..d33d73f --- /dev/null +++ b/INSTALL @@ -0,0 +1,13 @@ +Das Programm kann direkt gestartet werden. Eine Installation ist nicht +erforderlich. Die unten angegebenen Abhängigkeiten müssen erfüllt sein. + +apt-get install python3-cairo python3-gi python3-gi-cairo gir1.2-rsvg-2.0 \ + python-serial python3-nmea2 python3-smbus2 python3-bme280 + +Das Programm wird über eine Konfigurationsdatei obp60.conf im gleichen +Verzeichnis wie das Hauptprogramm gesteuert. Die Konfiguration wird +einmalig beim Programmstart eingelesen. + +Meßdaten werden im Homeverzeichnis unter ~/.local/lib/obp60 gespeichert. +Dies betrifft momentan Luftdruckmessungen mit dem BME280. +Das Verzeichnis wird automatisch angelegt. diff --git a/README b/README new file mode 100644 index 0000000..51c94b3 --- /dev/null +++ b/README @@ -0,0 +1,61 @@ +Multifunktionsdisplay (MFD) virtuell: OBP60v + +Hinweis: Dieses Programm dient in erster Linie dazu die GUI der "echten" +OBP60-Hardware zu designen. Eine eigenständige Nutzung ist selbstverständlich +"auf eigene Gefahr" hin möglich. + +Für Informationen zum OBP60 in Hardware siehe: + - https://open-boat-projects.org/de/diy-multifunktionsdisplay-obp-60/ + - https://obp60-v2-docu.readthedocs.io/de/latest/ + +Fehlermeldungen und Patches gerne an thomas@hoogi.de senden. + +Basishardware +- Raspberry Pi 4 + +Zusatzhardware: +- NMEA2000 Interface + - PiCAN-M (hiermit wird entwickelt) + - Waveshare RS485 CAN HAT (ungetestet) +- BME280-Sensor +- GPS über USB/seriell angeschlossen + +Zusatzsoftware: +- OpenCPN + +Abhängigkeiten +- python-can +- heapdict + +Für GPS +- python-serial +- python3-nmea2 + +Für BME280 +- smbus2 +- bme280 + +Zur Steuerung des Geräts sind 6 Tasten vorhanen. Numeriert von 1 bis 6 von +links nach rechts. Die Tasten können angeklickt werden und führen dann direkt +eine von der jeweiligen Seite abhängige Funktion aus. +Die jeweilige Funktion wird durch ein Symbol oberhalb der Taste dargestellt. +Die Tasten 3 und 4 sind für die Seitennavigation vorgesehen: zurück und vor. +Sie können jedoch von einer Seite bei Bedarf übersteuert werden. + +Wischgesten werden simuliert, indem die Maustaste auf einer Tastenfläche +gedrückt und auf einer anderen Taste losgelassen wird. + +Folgende Wischfunktionen sind implementiert: + 1. Programmende durch die Wischfunktion "2" -> "1" + 2. Tastensperre an: "6" -> "1" + 3. Tastensperre aus: "1" -> "6" + 4. Systemseite: "5" -> "6" + +Routen und Wegepunkte können von OpenCPN empfangen werden. Dazu muß eine +passende serielle Schnittstelle für den NMEA0183-Ausgang definiert werden. +Im System kann diese in der Datei rc.local aktiviert werden: + # Create virtual serial connection + socat pty,rawer,echo=0,group-late=dialout,mode=0660,link=/dev/ttyV0 \ + pty,rawer,echo=0,group-late=dialout,mode=0660,link=/dev/ttyV1 & +OpenCPN sendet dann Datensätze über ttyV0 und dieses Programm +empfängt sie über ttyV1. diff --git a/fonts/AtariST8x16.ttf b/fonts/AtariST8x16.ttf new file mode 100644 index 0000000000000000000000000000000000000000..f8f4b3e70c7b7291a7d6492cf4eb5afb68b8870c GIT binary patch literal 34688 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 literal 0 HcmV?d00001 diff --git a/fonts/DSEG7Classic-BoldItalic.ttf b/fonts/DSEG7Classic-BoldItalic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..de0b43a76ddcd48da693c9c7d9244c7ca955b5ad GIT binary patch literal 23464 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 XX_JlEy11}O*U$$Oy-Rk!!r1=-)9IBx literal 0 HcmV?d00001 diff --git a/images/Falling.png b/images/Falling.png new file mode 100644 index 0000000000000000000000000000000000000000..e0eca0a277b17a36b5f1166b39606eccb95f69f6 GIT binary patch literal 1142 zcmV-+1d02JP)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H11OrJ# zK~!jg?U_x897Pnz|F3&`*ce3<*-O@$sh;kI39R_B8^i?sN(AvDq6fjlMk7MRFATfrjPgT9{swQT}{{dsG6VSyCBcnztBNG!7H|b6!E6PTkigRF_Wi2{FJe5a6 zX`!T5%eL*Y0`ML)-o)A;a5*;U&Staud#)KKB=L+`K#du1NPWhQh)?8`mO6=zocH~o z0Dga+NO)pWh(p@5Nprk9ty5 zBmI-zG7kfgORq&ne5zC`ZOfch3Q`-X*Xx%lrWXg~-9@wTLB^a?k$P$MaYXebf#nj+ zAUy8*{#V&jQzP?B)0w}HgaxhX$5N~Ig;dwG1EGpb!-*1 zyvBP$@I5n*$r^*7b#40@9pfiK+j(Ti`0ek4u)g#%K6PEUq$|c|XfrZDKi@T&X928A z@@?q#J|iO173aUudO|!O1iupD?e!`haLV?}I^$fA%;k5(fJEd}#2EmONpc6VxyNgH z3tEFoXg{+vGn-NG1p=-}@)(Th)YQ~eQFpvPxm-Rhw%ZqD@T&vzy`AOdck+k_?9MeP1R`WaEv2wi*iU^0nXv^^ogXe0s+AVUsNg?icE7igGHYIX3;`{!e z4C8qK^0ZfbtJgjPfQN|+PQ`wULEnP-xh%1AU0QuyA&vv|imWl=C1N@R@bte@)a7_Q z48yBwVhrV`F(Nt&@cPor_(U=R0H$Ra52lSV6eGUxcL?t8>>oLec5kAab>Dp9Xcs$ZyIP>qx8jJTD`K8m7+q$U7>tzN2G$<+uI zi@$*Aj8dnR`(!BPcd}A_8pJ0UNOJtW)Chw+STV7oZ1#HCJk163ZUAQ!G~eZx%ez%$ z46SN;p4V=K!G31E4&d5mZ5gKW@*3)=H)iHW=zomZoB{CjCM`$XNFT4|EkuO-i0~nR zctZoiBQrBImdcnz+3cH9tyCX1i01(8l63;_tp~vmNypjpue!~!`L)?C-UZ-GS!WnZ z%WKPx0KnqK#Xr2je-ccy057kpYWrl%m)0OPA?zNE&jH@Ey|cRW+|trYl71s+1Xw$8 zwaVfM5YIQl@S8-Pn0aJ>)6>&d(zYWxBWW0k&8~wRMn;W)0AsuAn8>4v2mk;807*qo IM6N<$f+e9D3IG5A literal 0 HcmV?d00001 diff --git a/images/Falling_Fast.png b/images/Falling_Fast.png new file mode 100644 index 0000000000000000000000000000000000000000..80a28ff2629f7942a6172ea0e2f52976f0a75ff8 GIT binary patch literal 626 zcmV-&0*(ENP)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10sl!v zK~!jg?V8PM6hRb)&#jCk21OSxvYQ?=D;MHx_!>Tg58>K3kV!DnMRaEf=o5$tgCH|b zYZrmhUBF40&|Swx%={Qbw{UMLq|UCWpVMFW?R&ecTS(%6P|I@hZr1q(VD-E$*IM@m zgTd6o%M=7a2f*5Sqby6YGICZ6U$qiQY)T-pDS1=P`n^+CDgD+dfyAZ+5}VZae!E%rkd*rXAR+<8kqAyf%rezv zb^TGdeRsZz?N|)+{Eddc8VE&h3-IK33$w#tO1ONwJc*A1sDP-2)@fP30&w&XQ^HDB z?P&R{fq3svK8)NzB!p-6gLK2{Y)ON!x|><&g8&aM=|24O?Q5H4rRix>*&QxF z<3jYbA4yexw38HX#z4DjlPre_F|8`9~Q&Dg{Rp=qk~)sFH-*|pZghU zeR{kHa3RIA3=X?ier6ya$KwgG_{ie+^E@A0`I)|t1iE=B8PtwNnbeWl;v#OdofQCX zPOEamdw*i(WxE|j20(TIzOaMpF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10@g`H zK~!jg?OIK16HydB_a#+SS_rfYSIO8$7m6PX{SAVDMY`?}5Y(mk2W;&JQq2mthzoC<)GQpFp1=lb5M6vtZtrxiHfyS^v2r*vx(cvL|E02|?Ij3L=t zX(OW990bsmERQM5jirQg$km~dOFkXpA86H9S0Sg8$_j2!xTWE|ktHf6tAh>2EhW@YcbJ1 z6>UmJD$A8sf|zVmHc_oszqeX!G#bCaxVtX{W76}yZz|fg93XfEEDH~zt7~ow;D*%8nHZwC5%5X5y;;!=@ z8IClxW-VgAwWoxpLQCYuo*c0dawp?%O?v-)D3_c!44g@%KDfSjLWQHxabE76*VwHF z!Ex3Qr9J&J(hn?4j}zO7Da9d%bW8vljKo3=0~~FJoCg$VF`%QlJ_=xcizCO)5nVmn zMryU%XMm;vfEmx{eBV>$n61~pfbei5q8IZ)P}9|u>5J9Y$U=%!Z4$KEUQB8uNgA1q ztVhuVfR#USxc{4a9~|sF`~bkG17SFxMGKfY)8|;RcquDKZ>e-KD~I;+Ya0FnKPsMd T=nxzk00000NkvXXu0mjfq2z9C literal 0 HcmV?d00001 diff --git a/images/Rising.png b/images/Rising.png new file mode 100644 index 0000000000000000000000000000000000000000..6850dc60e65a4c26b331cb6364432d88160b8ad4 GIT binary patch literal 1044 zcmV+v1nc{WP)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H11EEPo zK~!jg?U_$#9CZ}OzwbArhME>U_%GDm`R(q+wM9@oNL8xwAR^`>iGOOP;GtrLDkv5_ zO1*@hyqQ9KP_Ps|cn~QDqh3|SLy@?%J2Tx{YN0I_(kAZge7z({c4xCQyPMgu?Dsaj z_kQnvc)xkSA2UJ{H-pr$3mB9}AX(E0Bx@RhWKAQGVMuFj6^q4Ol+8@Jmc+V0pDzSZ z(;_mffQj+t)!9OqI;pEN{4hdvy_%T_OeDsIs{T|0NHIPLzpdl{;xDYdBNp3Ob;;x_S$)0!J z%L0BE!<7KvtW+wiao0|5YL#=ji9ngB0Y2W{o5vl`JD6nsR5~(QDC`ZC@pC)~{M8_j zB+ap!+UkO;U!aKGbu-?YY*EbC)HopZd}v0n2ItRqt*=6TCHDv+ob^;gAk zX36(!QL$s=h=1Kz7uVtzB^)T{S-}{ zLi0nyM0k*!jYK3O)g_t;KX%1g}KJ+}sdu~e`BnPN>nX=vYm2d)3rkDkpm0?C?2AX(E0Bx|~9K>h{2f@ai_iy_GX O0000=G`P)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10mey0 zK~!jg?U>O{6G0S)zq3=M!32%oNJ86P+BMPi35fav1TIZb;EtEl7t;${O%v2hO$m^6 z=Xj&g2GL%eLndUttL=0;f4-cvyVDU>tkkl4z1PcbRMmS)(Nn>_>InDQEZMz zqwm(4E){8qu+KFK0BIEsuRStdL?VA_tII@nX)p8E>Ua9Jg!mDVo8~mav+;QR!7|sp zA`+S-{viPn9NsCWA|fInZ~i}153IK=ri;~8^%B@xVrs9hsuz~orXn#e|DK33nzonO z&0bYiJ1Ad)hfDtC$3-*Su-dYiRiqVVkL4u*9<+;<9kYC^g|yH$;N9XN$1FU-^gSY% zEufYM86~r!Svds@iyD`Ty0uC9+6rWRcX!BB_x@QX`9`MixnpERq^o zBsH=~YGjer$ReqcMN%V+q=wlc_6>Xk!rW)KgJ#!5?AzoQwp4KoX9(L500000NkvXX Hu0mjfp-l-Q literal 0 HcmV?d00001 diff --git a/images/Rising_Very_Fast.png b/images/Rising_Very_Fast.png new file mode 100644 index 0000000000000000000000000000000000000000..a08d85ca4c05708f07e591819ce3f23d6d12cf91 GIT binary patch literal 847 zcmV-V1F-ywP)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10^CVN zK~!jg?OH)k6G0e#ZvmxZ^ri&qw%xW-W21@IqlpM68e;+nh$(2i@s|icK~LVjdGnx` zCLU0znkbQriMEMi=kri1+l6IzW;ZD@^KLt{^X9#NGdr)_Qbh1Sz)MR|DHiXii2Do*=ot(Q3Ul;VAp%oAlIc01%*^>r6~G zzcqi_=&`Oy=j5q{z6k(Opz$Y4ulsKdfr($a&b+N2YaOffRjizLvk;oC<=mLLcU0B&h!YM4eABO|>?!f73+ zbi2#3ol31Fn0Kn=m+qKoGcwYTBsx&yBTGM$WHs0RoCzaJG?HkEAvZQ!tvBJea3s;e zG_oYFNHmh@l^opA;YgyxUS!cf0r!yDh-rKfqEWBc$NJZYMG`H%dU)HhNNjXHSXfr$}3(VH?Q|!!@D?D5o={JgF_3{Dk<%2Vl88SOZ692#-1r^&u`5J2aZptBKn~-e)ojp#gzi$93USc0N Z`~|cI#r}m{c~bxY002ovPDHLkV1l4}emDRC literal 0 HcmV?d00001 diff --git a/images/Stationary.png b/images/Stationary.png new file mode 100644 index 0000000000000000000000000000000000000000..3b748af432b1510026372e1fdf9e9c442d56fe77 GIT binary patch literal 559 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nC-&H|6fVg?4j!ywFfJby(BP*AeO zHKHUqKdq!Zu_%?Hyu4g5GcUV1Ik6yBFTW^#_B$IX1_s7Go-U3d8I5meocFpDAaV43 zr_wH|RV9m8aCYiH+_gUN%_9EC);j`@HJ$L#Y~FEkZqsbdyDuGHt0)^kaLW{2^=R&# zye&&hRnAFN=@-@Cu9@@u?~ebI0)*n28sD;=I@G#*%gAt1B%x!K-#JN^Yy*_!qRPV9e{YqX=LT<7Q#?5Ab_bV1CCV5wGsJmom`Q!D? zxgO#Nwk-YE_O!!gt^2xdQER{bEt2}RWy6Un4dKd_cC3GPy6;!quwY6tA={Fy_DLk>lT+^Q6>dFP*-kRK=ctu+6+#-(+{tNP$a+ha*do$fF pd-mR(xHI27um<7b?j6VUC+?fAx@?Q^1Yl${c)I$ztaD0e0suDy{?`Bi literal 0 HcmV?d00001 diff --git a/images/alarm.png b/images/alarm.png new file mode 100644 index 0000000000000000000000000000000000000000..c4fd00357e302685c5d12dd3fa37d1ec4988dd84 GIT binary patch literal 150 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!93?!50ihlx9oB=)|u0R?H{{R2qeE0k%Ad9gi z$S;_|;n|HeAjjI%#W95Ax@q@DUIqo8!!e!j6bb|U5*Ga5?_@RMV3!EHz=Z2JGS6hJ s>ECRV-F3kt;zr8Ux7SYmKK(ava;MP!&ulmQ*;7A=KpG|{>AZS%c6{z6Rf2UNEKDBVEi9EUB2q%wh42m l`0m@kW1n{Ll}Rhv#5higNH2cBGzDlhgQu&X%Q~loCII^WGuHqB literal 0 HcmV?d00001 diff --git a/images/arrow_r1.png b/images/arrow_r1.png new file mode 100644 index 0000000000000000000000000000000000000000..fa306848db924afa002fbdf7d351f18e6995033a GIT binary patch literal 135 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!93?!50ihlx9oB=)|u0R?H{{R2qeE0k%Ad9gi z$S;_|;n|HeAV85tQ29UP2;3k#H3lo-R9LS>kY b88{fKmUB+DKgg^P)W_iI>gTe~DWM4f_(UK# literal 0 HcmV?d00001 diff --git a/images/bee.png b/images/bee.png new file mode 100644 index 0000000000000000000000000000000000000000..9ba2a01af1da94c4ea4509443ad3aadc742cf83e GIT binary patch literal 157 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!93?!50ihlx9oB=)|u0R?H{{R2qeE0k%Ad9gi z$S;_|;n|HeAjiqm#W95Ax@_k`&I1NKM^Y^Q3m33!S=5V9`WwEoQ_ku5!PJWkgkS(6BelF{r5}E)&PBOm$ literal 0 HcmV?d00001 diff --git a/images/buzzer.png b/images/buzzer.png new file mode 100644 index 0000000000000000000000000000000000000000..d9a8864949822fc1a5c0ca4541251651bf6108d0 GIT binary patch literal 124 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!93?!50ihlx9oB=)|u0R?H{{R2qeE0k%Ad9gi z$S;_|;n|HeAV<;D#W95AdU8TTgMfhGD+VQpGYo7tE*cXOT$B#%TJVg4;X?_V|0fxt QX&^&9UHx3vIVCg!01LDsrT_o{ literal 0 HcmV?d00001 diff --git a/images/fuelcan.png b/images/fuelcan.png new file mode 100644 index 0000000000000000000000000000000000000000..c0044f9fd5faaddbbe342ceedf99a7a1f873ad58 GIT binary patch literal 4750 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 NJb6X#BWo&t`Cnms@~Z#< literal 0 HcmV?d00001 diff --git a/images/lightoff.png b/images/lightoff.png new file mode 100644 index 0000000000000000000000000000000000000000..624dfeedd90a1d1c6b8e84ac81a12fe70f18f09e GIT binary patch literal 140 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!93?!50ihlx9oB=)|u0R?H{{R2qeE0k%Ad9gi z$S;_|;n|HeAjiTXOu g`09YwfhtD^hU9%r>&(}^`3f@B)78&qol`;+0GLoIvH$=8 literal 0 HcmV?d00001 diff --git a/images/lighton.png b/images/lighton.png new file mode 100644 index 0000000000000000000000000000000000000000..7a3f5fed2d27ea963b726891577c8dff9aa08078 GIT binary patch literal 144 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!93?!50ihlx9oB=)|u0R?H{{R2qeE0k%Ad9gi z$S;_|;n|HeAjj0x#W95AdU65-vzvlgLW)2_LSjNeLP7#-QriUqje`C-VRR literal 0 HcmV?d00001 diff --git a/images/lock.png b/images/lock.png new file mode 100644 index 0000000000000000000000000000000000000000..f04f8be77b4324e5e47a72c68f5a5ac4c177c1e4 GIT binary patch literal 143 zcmeAS@N?(olHy`uVBq!ia0vp^JU}eK!VDxY_&1vXDgFST5LX}#2mk;7_up`EK9J8? z666=m;PC858jz#s>Eal|aXmR9K|myhfys^0jZs0B!;Qy5P1#D7gH3_2gL`?yEd$LE d)da3ZvJ3?=Op0E`o5exKdb;|#taD0e0s!N7A7=mn literal 0 HcmV?d00001 diff --git a/images/ship.png b/images/ship.png new file mode 100644 index 0000000000000000000000000000000000000000..5e1e24dfd51b8a431b14d08e655a5717cbc245b6 GIT binary patch literal 146 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnH3?%tPCZz)@o&cW^S0D`m|Ns9t4Hs+yau`d3 z{DK)Ap4~_Ta!fp3977~7Cnq?t@dnJwWSnwvE7RPCTe;?L%nD^zWV^g7l$*nFN7&jZ jM_x9=vWJNZGW=`|-j9t%m3SC;flT&v^>bP0l+XkK_xdDW literal 0 HcmV?d00001 diff --git a/images/swipe.png b/images/swipe.png new file mode 100644 index 0000000000000000000000000000000000000000..449a80a31fcd5d7cf8abeeddba7286f6e1b9a9dd GIT binary patch literal 143 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 Nl`>JWZStI)-vFl$wxs|7 literal 0 HcmV?d00001 diff --git a/images/wmo0.png b/images/wmo0.png new file mode 100644 index 0000000000000000000000000000000000000000..556220e03b72dac4d14f68d8763891bf858f0b17 GIT binary patch literal 543 zcmV+)0^t3LP)$!D=UgYS zr5YMgX5cSKI&_62DeF1 z)B*=)wjXkiYzyJO9JrDDzCzrGS^{7~;>UoC6#ys$t^_bpsCXEF5s9AzC9wjKJw2TX zVAzNFe*p$1ehDOs0dORMF<;_=0f2sq-vZ@60fKHsQ;PFumj3Tv^J@#>zE|!afl8n5 zgH~W#aly=9y}JLa0zik{Ka=M`5p@M-6c^3x$-8^Bm;ab>t`ax_>VZZxyN?w>5DB2o h%nqXv{}UjN_5}s+~5EJ literal 0 HcmV?d00001 diff --git a/images/wmo1.png b/images/wmo1.png new file mode 100644 index 0000000000000000000000000000000000000000..b87eb3fec06cab3b76ba5e0fcf98bf3451ed429c GIT binary patch literal 518 zcmV+h0{Q)kP)3vegN+w_q|0;V7^6YW@ErL0pYl$JD+)y6yXj*4ITK3 zgJ%L}HVU+WK44PPjjsXU1h#;F;4q0e(EBO?MhJ@P2bfB-iX?d@unAOwV@a2Z=zC27 zr~z$)%C9G-_eo&GnK+TuOibTP0>B{fChE9o=AR@1pf(9byfN1&P&eWwH(Zrqa;cF4>w0if>a z+dwT#`ltk!-MBC5B~yAr4y9&hz#MQU={a`;dAGuUP762i3yvxDkggyL$N&HU07*qo IM6N<$f;#Wk4gdfE literal 0 HcmV?d00001 diff --git a/images/wmo2.png b/images/wmo2.png new file mode 100644 index 0000000000000000000000000000000000000000..80cbae08acfa063d5fed254a2ee7bb871f988230 GIT binary patch literal 510 zcmV|4j`RzM1yC#VuN2yTtRv0idE7SmRYGySv8}xcw zQnCwJ00cY%WuKbV1U9n!j-;=)5&))wU*H`W@U2TpU<)u{S5m{b$WrVBW;O;~0YAW~ zq(|S{R0KAG4q!izI3yroX2ZZO&;?9Ly2(?YfIkd;cS6`q?b~8_*YFseNECwbox95fJxj3 z$0txpkv=bh4crRYlk}1#J)Xz0nT-I~xc5IU={}VKUjkL!?st$<96Sh^*${AtJM*U` zU8FSNLtq&w14pUF!M_LQfLBTNLJYKW6#jSo2ScpGHa9@;#{d8T07*qoM6N<$f>e~x A1^@s6 literal 0 HcmV?d00001 diff --git a/images/wmo3.png b/images/wmo3.png new file mode 100644 index 0000000000000000000000000000000000000000..abcff9d39ddc6ff94187647f321ec87e88a892ec GIT binary patch literal 592 zcmV-W0B864D zRAQ4Rf})k6NhF1C1VPtAw9uRsdkYcw?Yy`1+=0h1Gxy9n_b@Z}nIziTI`Z7y%t~z& zU}pWmfth_Vv%hN?%pv`B z-un-dE`m{!vL6Kg0cSud4K|ho3xEM1CDqaZ%`?c{%mER{r-PW zOAakM;3fAD@BkQ)^fT=q4IMDE?g0E8&;@+WLJr)03xG-B7TEm32@?MWuJTF<6l4_R{>7=)(QUtkAY1ePqj)ODxd@S z?xgG9SE>JQ=P$rTyZ|npTtFoTd9VN%vFPMApr;)Iiog!A6pLI^x?*Hz!@!=TqhL_8 eJ<|xtqx}bJj>Ynb1#B_^0000~}U&Kt+|FE{-7)t#7X_2?&h&f5YcuQvFHUedUE;f4I-!A)O(KEe!t+^z zhK$}%-$?I>`>OR)gNcE2hBo8ID|4#fwU YwZ+7Y?RT>s20DVl)78&qol`;+0NQ;{m;e9( literal 0 HcmV?d00001 diff --git a/images/wmo5.png b/images/wmo5.png new file mode 100644 index 0000000000000000000000000000000000000000..4663949ea27bef5506fd7f1730f61afdddc1dadd GIT binary patch literal 584 zcmV-O0=NB%P) zpj0_fN;!;=_=%$t@wEO$WxAt~!+>Hx+w2ouoT%!&o>^ID3c&Bj((uGN^r3SnLv+ne>)Ksbg?tsj;nZN8hseH9m3 ze_f0xTE#eDv3?xiv4yjB08HT*HgMn{>x&7vP`O9H#|TeTVuf)KtsZhZ~sm=l*z@i)y{+AVj8C;D>vfH?h%uC?QI`eH9(mnw9XPXOqW#(D{0000Xw|0x literal 0 HcmV?d00001 diff --git a/images/wmo7.png b/images/wmo7.png new file mode 100644 index 0000000000000000000000000000000000000000..0ae1e45ac9b2980a6870c36aa3056dfa9e9fe1ff GIT binary patch literal 507 zcmVV-9kTiWTquBTOq&A~#EXJh9Sh)c0ldM|7y$dQSrAv60~Ey70(gbV7y$e6 zt{^Th3ZQo*%FG?yt?iv5CI7d?vsgHUk7eOpQv(J21NUlsySag~a0FkyoE4b8ASR!2?NVzQ@A?xG^-( zU;_{lr|=p(IGLH-v*ws;KSxkUjK#Pfn!8w=o40ld|7cwJh=H}Fer=tlS_ xnCeP%&zP$d{xM<1Br|t=pI=G`P)j2n+PnGa=y*p9!_ZV**;BF;*gy=ewm)%gU0N@b*U=jP5eLGVCFP6ZD3V<;@TdCqP zUPq8P+6KU-3jPM0SMa%3J`dXf*o7G!Tjhwh;d?lEWxJvOO<6{tUh1Gmp0VunrQwLKKv0|Q9j^JPLh$%eHRg$y{(8T@%$t%0ALe((700000NkvXX Hu0mjflJNVR literal 0 HcmV?d00001 diff --git a/nmea2000/__init__.py b/nmea2000/__init__.py new file mode 100644 index 0000000..16ba3d3 --- /dev/null +++ b/nmea2000/__init__.py @@ -0,0 +1,3 @@ +from .device import Device +from .boatdata import BoatData +from .hbuffer import History, HistoryBuffer diff --git a/nmea2000/boatdata.py b/nmea2000/boatdata.py new file mode 100644 index 0000000..48e753a --- /dev/null +++ b/nmea2000/boatdata.py @@ -0,0 +1,567 @@ +''' +!!! Dies ist noch im Ideen-Stadium +WIP TBD + +Die Werte der Daten werden wie in NMEA2000 gespeichert: +Längen: m +Geschwindigkeiten: m/s +Winkel, Kurse: radiant (Bogenmaß) +Temperaturen: K +Druck: Pa +Geokoordinaten sind eine vorzeichenbehaftete Fileßkommazahl + +Liste der Daten mit Langbezeichnung siehe: valdesc.py + +Die format()-Funktion liefert immer einen String zurück. +Unterscheidung zwischen "kein Wert" und "ungültiger Wert"? + +Normale Daten +------------- +ALT - Altitude, Höhe über Grund +AWA - Apparant Wind Angle, scheinbare Windrichtung +AWS - Apparant Wind Speed, scheinbare Windgeschwindigkeit +BTW - Bearing To Waipoynt, Winkel zum aktuellen Wegpunkt +COG - Course over Ground, Kurs über Grund +DBS - Depth Below Surface, Tiefe unter Wasseroberfläche +DBT - Depth Below Transducer, Tiefe unter Sensor +DEV - Deviation, Kursabweichung +DTW - Distance To Waypoint, Entfernung zum aktuellen Wegpunkt +GPSD - GPS Date, GPS-Datum +GPDT - GPS Time, GPS-Zeit als UTC (Weltzeit) +HDM - Magnetic Heading, magnetischer rechtweisender Kurs +HDT - Heading, wahrer rechtweisender Kurs +HDOP - GPS-Genauigkeit in der Horizontalen +LAT - Latitude, geografische Breite +LON - Longitude, geografische Höhe +Log - Log, Entfernung +MaxAws - Maximum Apperant Wind Speed, Maximum der relativen Windgeschwindigkeit seit Gerätestart +MaxTws - Maximum True Wind Speed, Maximum der wahren Windgeschwindigkeit seit Gerätestart +PDOP - GPS-Genauigkeit über alle 3 Raumachsen +PRPOS - Auslenkung Sekundärruder +ROT - Rotation, Drehrate +RPOS - Rudder Position, Auslenkung Hauptruder +SOG - Speed Over Ground, Geschwindigkeit über Grund +STW - Speed Through Water, Geschwindigkeit durch das Wasser +SatInfo - Satellit Info, Anzahl der sichtbaren Satelliten +TWD - True Wind Direction, wahre Windrichtung +TWS - True Wind Speed, wahre Windgeschwindigkeit +TZ - Time Zone, Zeitzone +TripLog - Trip Log, Tages-Entfernungszähler +VAR - Variation, Abweichung vom Sollkurs +VDOP - GPS-Genauigkeit in der Vertikalen +WPLat - Waypoint Latitude, geogr. Breite des Wegpunktes +WPLon - Waypoint Longitude, geogr. Länge des Wegpunktes +WTemp - Water Temperature, Wassertemperatur +XTE - Cross Track Error, Kursfehler + +Normale Daten erweitert +----------------------- + +ROLL - Roll - Krängen / Rotation in Querrichtung +PTCH - Pitch - Rollen / Rotation in Längsrichtung +YAW - Yaw - Gieren / Rotation um die Senkrechte Achse + +XDR-Daten +--------- +xdrVBat - Bordspannung +xdrHum - Luftfeuchte +xdrPress - Luftdruck +xdrTemp - Temperatur + +xdrRotK - Kielrotation +xdrRoll +xdrPitch +xdrYaw + +''' + +import datetime +import time +import math +import random + +class BoatValue(): + ''' + Wert mit Datentyp, Einheit, Validekennzeichen und Skalierungsfaktor + ''' + + placeholder = '---' + + def __init__(self, shortname, unit=''): + self.valname = shortname + self.unit = unit + self.value = None + self.valid = False + self.resolution = 1 + self.decpl = None # decimal places for format + self.desc = "" # long description + self.timestamp = time.time() + self.simulated = False + self.history = False + self.hbuf = None + + def getValue(self): + # Wert unter Beachtung der Unit zurückgeben + if self.value and valid: + return self.value + else: + return self.placeholder + + def getValueRaw(self, ignorevalid=False): + if ignorevalid or self.valid: + return self.value + return None + + def setValue(self, newvalue): + self.value = newvalue + self.timestamp = time.time() + self.valid = True + # if self.history: + # TODO es kann mehrere verschiedene Zeitreihen geben! + # Implementierung nich unklar. + + def enableHistory(self, size, delta_t): + if self.history: + # Wiederholter Aufruf löscht die bisherige Historie + self.hbuf.clear() + return + self.history = True + self.hbuf = HistoryBuffer(size, delta_t) + + def format(self): + if self.simulated: + if self.valname == "xdrVBat": + return "{:.1f}".format(random.uniform(11.8, 14.2)) + else: + return "{:.0f}".format(random.uniform(95, 130)) + if not self.value or not self.valid: + return self.placeholder + if not self.decpl: + if self.value < 10: + self.decpl = 1 + else: + self.decpl = 0 + return "{0:3.{1}f}".format(self.value, self.decpl) + + def __str__(self): + out = self.valname + #out += self.format() + if not self.valid: + out += (" (invalid)") + return out + +class BoatValueGeo(BoatValue): + # geofmt = lat | lon + # unit = rad | deg + def __init__(self, shortname, geofmt, unit='deg'): + super().__init__(shortname, unit) + self.geofmt = geofmt + self.decpl = 3 + #print(self.valname) + def format(self): + # latitude, longitude + if not self.value or not self.valid: + return self.placeholder + degrees = int(self.value) + minutes = (self.value - degrees) * 60 + if self.geofmt == 'lat': + direction = ('E' if self.value > 0 else 'W') + formatted = "{0}° {1:.{3}f}' {2}".format(degrees, minutes, direction, decpl) + elif self.geofmt == 'lon': + direction = 'N' if self.value > 0 else 'S' + formatted = "{0}° {1:.{3}f}' {2}".format(degrees, minutes, direction, decpl) + else: + formatted = str(self.placeholder) + return formatted + +class BoatValueDate(BoatValue): + # datefmt = GB | US | DE | ISO + def __init__(self, shortname, datefmt='ISO'): + super().__init__(shortname) + self.datefmt = datefmt + def format(self): + if self.datefmt == 'DE': + formatted = self.value.strftime("%d.%m.%Y") + elif self.datefmt == 'GB': + formatted = self.value.strftime("%d/%m/%Y") + elif self.datefmt == 'US': + formatted = self.value.strftime("%m/%d/%Y") + elif self.datefmt == 'ISO': + formatted = self.value.strftime("%Y-%m-%d") + return formatted + +class BoatValueTime(BoatValue): + def __init__(self, shortname, timezone='UTC'): + super().__init__(shortname) + self.tz = timezone + self.timefmt = 'hh:mm:' # TODO hh:mm:ss | ...? + def format(self): + formatted = self.value + return formatted + +class BoatValueSpeed(BoatValue): + # unsigned? Was ist mit Rückwärts? + def format(self): + if self.simulated: + return "5.3" + if not self.value or not self.valid: + return self.placeholder + if self.value < 20: + formatted = f"{self.value:3.1f}" + else: + formatted = f"{self.value:3.0f}" + return formatted + +class BoatValueAngle(BoatValue): + # course, wind, heading, bearing + # roll, pitch, yaw, rudder, keel + def format(self): + if self.simulated: + if self.valname == "BTW": + return "253" + else: + return "120" + if self.value: + return f"{self.value:03.0f}" + else: + return self.placeholder + +class BoatValueRotation(BoatValue): + # signed + def format(self): + if self.value < 10 and self.value > -10: + formatted = f"{self.value:.1f}" + else: + formatted = f"{self.value:.1f}" + return formatted + +class BoatValueDepth(BoatValue): + # unsigned + def format(self): + if self.simulated: + if self.valname == "DBT": + return "6.2" + else: + return "6.5" + if not self.value or not self.valid: + return self.placeholder + if self.value < 100: + formatted = f"{self.value:3.1f}" + else: + formatted = f"{self.value:3.0f}" + return formatted + +class BoatValueDistance(BoatValue): + # unsigned integer? + def format(self, signed=False): + if self.value: + return f"{self.value:d}" + else: + return self.placeholder + +class BoatValueTemperature(BoatValue): + # signed + def __init__(self, shortname, unit='K'): + super().__init__(shortname, unit) + self.instance = None + self.sensortype = None + self.decpl = 0 + def format(self): + if self.value < 100: + formatted = f"{self.value:3.1f}" + else: + formatted = f"{self.value:3.0f}" + return formatted + +class BoatValueHumidity(BoatValue): + # unsigned integer + # range 0 .. 100 + def __init__(self, shortname, unit='%'): + super().__init__(shortname, unit) + self.instance = None + self.sensortype = None + self.decpl = 0 + def format(self): + return f"{self.value:d}" + +class BoatValuePressure(BoatValue): + # unsigned integer + # range ca. 800 .. 1100 for athmospheric pressure + def __init__(self, shortname, unit='Pa'): + super().__init__(shortname, unit) + self.instance = None + self.sensortype = None + self.decpl = 1 + def format(self): + if self.value and self.valid: + return f"{self.value:4.{self.decpl}f}" + else: + return self.placeholder + +class Tank(): + + def __init__(self, instance=0): + self.instance = instance + self.fluidtype = 1 # water -> lookup + self.volume = None + self.capacity = None + self.desc = "" # long description + + def __str__(self): + out = f" Tank #{self.instance}" + out += f" Capacity: {self.capacity} l\n" + out += f" Fluid level: {self.volume} l\n" + return out + +class Engine(): + + def __init__(self, instance=0): + self.instance = instance + self.speed_rpm = None + self.exhaust_temp = None + self.desc = "" # long description + + def __str__(self): + out = f" Engine #{self.instance}\n" + if self.exhaust_temp: + out += f" Exhaust temp: {self.exhaust_temp:.1f} °C\n" + else: + out += " Exhaust temp: no data\n" + return out + +class Satellite(): + + def __init__(self, prn_num): + self.prn_num = prn_num + self.elevation = None + self.azimuth = None + self.snr = None # signal noise ratio + self.rres = None # range residuals + self.status = None # lookup -> prnusage + self.lastseen = None + + def __str__(self): + out = f"SAT {self.prn_num:02d}: " + if self.snr: + out += f"snr={self.snr}dB elevation={self.elevation:.4f} azimuth={self.azimuth:.4f} status={self.status}\n" + else: + out += "no signal\n" + return out + +class SatelliteList(): + + def __init__(self): + sat = {} + rrmode = None + maxage = 300 # sec + def getCount(self): + return len(sat) + def addSat(self, pnr_num): + pass + def delSat(self, pnr_num): + pass + + +class BoatData(): + + def __init__(self): + + self.simulation = False + + # nach Überschreiten dieser Schwelle in Sekunden wird + # ein Meßwert als ungültig angesehen + self.maxage = 5 + + # Systemspannung; temporär. später auf bessere Weise speichern + self.voltage = BoatValue("xdrVBat", "V") + + # Navigationsdaten + self.awa = BoatValueAngle("AWA", "kn") + self.aws = BoatValueSpeed("AWS", "kn") + self.twd = BoatValueAngle("TWD", "kn") + self.tws = BoatValueSpeed("TWS", "kn") + self.lat = BoatValueGeo("LAT", "lat", "deg") + self.lon = BoatValueGeo("LON", "lon", "deg") + self.gpsd = BoatValueDate("GPSD", "ISO") + self.gpst = BoatValueTime("GPST") + self.sog = BoatValueSpeed("SOG", "kn") + self.cog = BoatValueAngle("COG", "deg") + self.xte = BoatValueDistance("XTE", "m") + self.stw = BoatValueSpeed("STW", "kn") + self.dbt = BoatValueDepth("DBT", "m") + self.roll = BoatValueAngle("ROLL", "deg") + self.pitch = BoatValueAngle("PTCH", "deg") + self.yaw = BoatValueAngle("YAW", "deg") + self.rpos = BoatValueAngle("RPOS", "deg") + self.prpos = BoatValueAngle("PRPOS", "deg") + + # Nächster Wegepunkt + self.wpno = BoatValue("WP") + self.wpname = BoatValue("WPname") + self.wplat = BoatValueGeo("WPLat", "lat", "deg") + self.wplon = BoatValueGeo("WPLon", "lon", "deg") + self.wpdist = BoatValueDistance("DTW", "m") + self.bearing = BoatValueAngle("BTW", "kn") + + # Umgebung + self.temp_water = BoatValueTemperature("WTemp", "°C") + self.temp_air = BoatValueTemperature("xdrTemp", "°C") + self.pressure = BoatValuePressure("xdrPress", "hPa") + self.humidity = BoatValueHumidity("xdrHum", "%") + self.temp = {} # Erweiterte Temperaturdaten + self.press = {} # Erweiterte Druckdaten + + # Sonderdaten + self.rotk = BoatValueAngle("xdrRotK", "deg") # Kielrotation + + # Maschinen + self.engine = {} + + # Tanks + self.tank = {} + + # Mehrere getrennte Batteriekreise + # - Starter + # - Verbrauchen + # - Ankerwinsch / Bugstrahlruder + + # Stromerzeugung + # Solarleistung + # Generatorleistung + # Benzingenerator + # Windgenerator + # Wasser-/Schleppgenerator + # Maschine Rekuperation + + # Satelliten + self.sat = {} + + # Zeitreihen für diverse Daten + self.history = {} + + self.valref = { + 'AWA': self.awa, + 'AWS': self.aws, + 'BTW': self.bearing, + 'COG': self.cog, + 'DBT': self.dbt, + 'DTW': self.wpdist, + 'GPSD': self.gpsd, + 'GPST': self.gpst, + 'LAT': self.lat, + 'LON': self.lon, + 'PRPOS': self.prpos, + 'PTCH': self.pitch, + 'RPOS': self.rpos, + 'ROLL': self.roll, + 'SOG': self.sog, + 'STW': self.stw, + 'TWD': self.twd, + 'TWS': self.tws, + 'WTemp': self.temp_water, + 'WPLat': self.wplat, + 'WPLon': self.wplon, + 'XTE': self.xte, + 'xdrRotK': self.rotk, + 'xdrVBat': self.voltage, + 'xdrTemp': self.temp_air, + 'xdrPress': self.pressure, + 'xdrHum': self.humidity, + 'YAW': self.yaw + } + + def addTank(self, instance): + self.tank[instance] = Tank(instance) + + def addEngine(self, instance): + self.engine[instance] = Engine(instance) + + def addSensor(self, sensortype, instance): + if sensortype == 'temp': + if not instance in self.temp: + self.temp[instance] = BoatValueTemperature() + else: + raise ValueError(f"duplicate key '{instance}'") + elif sensortype == 'press': + if not instance in self.press: + self.press[instance] = BoatValuePressure() + else: + raise ValueError(f"duplicate key '{instance}'") + + def updateSatellite(self, prn_num, elevation, azimuth, snr, rres, status): + if not prn_num in self.sat: + self.sat[prn_num] = Satellite(prn_num) + self.sat[prn_num].elevation = elevation + self.sat[prn_num].azimuth = azimuth + self.sat[prn_num].snr = snr + self.sat[prn_num].rres = rres + self.sat[prn_num].status = status + self.sat[prn_num].lastseen = time.time() + + def __str__(self): + out = "Boat Data\n" + out += f" Voltage: {self.voltage}\n" + out += f" Latitude: {self.lat.value}\n" + out += f" Longitude: {self.lon.value}\n" + out += f" SOG: {self.sog}\n" + for e in self.engine.values(): + print(e) + for t in self.tank.values(): + print(t) + out += " Satellite info\n" + for s in self.sat.values(): + out += str(s) + return out + + def updateValid(self, age=None): + # age: Alter eines Meßwerts in Sekunden + if not age: + age = self.maxage + t = time.time() + for v in vars(self).values(): + if isinstance(v,BoatValue): + if t - v.timestamp > age: + v.valid = False + + def getRef(self, shortname): + ''' + Referenz auf ein BoatValue-Objekt + ''' + try: + bv = self.valref[shortname] + except KeyError: + bv = None + return bv + + def getValue(self, shortname): + ''' + Wert aufgrund textuellem Kurznamen zurückliefern + ''' + try: + value = self.valref[shortname].value + except KeyError: + value = None + return value + + def setValue(self, shortname, newvalue): + ''' + Rückgabewert True bei erfolgreichem Speichern des Werts + ''' + if not shortname in self.valref: + return False + field = self.valref[shortname] + field.value = newvalue + field.timestamp = time.time() + field.valid = True + return True + + def enableSimulation(self): + self.simulation = True + for v in self.valref.values(): + v.simulated = True + + def addHistory(self, history, htype): + """ + htype: press, temp, hum + """ + self.history[htype] = history diff --git a/nmea2000/device.py b/nmea2000/device.py new file mode 100644 index 0000000..06a950f --- /dev/null +++ b/nmea2000/device.py @@ -0,0 +1,63 @@ + +''' +Platzhalter WIP +- ausprogrammieren nach Bedarf +Geräteliste + - wird regelmäßig aktualisiert + +''' + +import time +import struct + +class Device(): + + def __init__(self, address): + #WIP + #Felder können sich noch ändern! + self.address = address + self.instance = 0 # default 0 + self.sysinstance = 0 # used with bridged networks, default 0 + self.lastseen = time.time() + self.uniqueid = None + self.manufacturercode = None + self.industrygroup = None + self.name = None # User defined device name + self.product = None # Product name + self.productcode = None # Product code + self.devicefunction = None + self.deviceclass = None + self.serial = None + self.modelvers = None # Hardware Version + self.softvers = None # Current Software Version + self.n2kvers = None # NMEA2000 Network Message Database Version + self.certlevel = None # NMEA2000 Certification Level + self.loadequiv = None # NMEA2000 LEN + + # Configuration Info + self.instdesc1 = None + self.instdesc2 = None + self.manufinfo = None + + def getName(self): + # NAME errechnen aus den Claim-Daten + # TODO Das hier ist noch fehlerhaft! + data = bytearray() + data.append((self.deviceclass << 4) | (self.devicefunction & 0x0f)) + data.extend(struct.pack(' self.size: + # too old start new + self.clearfile + self.head = 0 + else: + # usable data found, fix missing + self.fp.seek(32) + data = self.fp.read(self.bytesize * self.size) + i = 0 + for d in range(0, self.size, self.bytesize): + if self.bytesize == 1: + self.buf[i] = data[d] + elif self.bytesize == 2: + self.buf[i] = data[d] + data[d+1] * 256 + elif self.bytesize == 3: + self.buf[i] = data[d] + (data[d+1] << 8) + (data[d+2] << 16) + elif self.bytesize == 4: + self.buf[i] = data[d] + (data[d+1] << 8) + (data[d+2] << 16) + (data[d+3] << 24) + i += 1 + # add empty data for missing steps + for s in range(missing): + self.add(0) + return True + + def finish(self): + if not self.fp.closed: + self.fp.close() + + def add(self, value): + # check if add request perhaps too early + timestamp = int(time.time()) + if timestamp - self.headdate < self.dt * 0.98: # a little bit of tolerance + print("add new value too early, ignored") + return False + self.headdate = timestamp + self.buf[self.head] = value + self.updatefile(value) + self.head += 1 + if self.head == self.size: + self.head = 0 + return True + + def get(self): + """ + Return buffer in linear sequence, newest values first + """ + for i in range(self.head -1, -1, -1): + yield self.buf[i] + for i in range(self.size - 1, self.head -1, -1): + yield self.buf[i] + + def getvalue(self, delta): + """ + Return a single value dt seconds ago + delta has to be smaller than self.dt * self.size + TODO check if value is missing, perhaps allow tolerance (+/- ) + """ + index = self.head - abs(delta) // self.dt + if index < 0: + index = self.size - index - 1 + return self.buf[index] + + def getvalue3(self, delta): + """ + same as getvalue but calculate mean with two neighbor values + TODO check for missing values (=0) + """ + index = self.head - abs(delta) // self.dt + if index < 0: + index = self.size - index - 1 + ixprev = index - 1 + if ixprev < 0: + ixprev = self.size - 1 + ixnext = index + 1 + if ixnext > self.size - 1: + ixnext = 0 + return round((self.buf[ix] + self.buf[ixprev] + self.buf[ixnext]) / 3) + + def setname(self, newname): + """ + set new name in buffer and storage backend + """ + self.name = newname[:16] or '' + self.fp.seek(4) + fp.write(self.name.ljust(16, ' ').encode()) + + def createfile(self): + """" + Creates new file from current buffer + """ + with open(self.filename, 'wb') as fp: + fp.write(self.magic) + fp.write(self.name.ljust(16, ' ').encode()) + fp.write(struct.pack('B', self.bytesize)) + fp.write(struct.pack('H', self.size)) + fp.write(struct.pack('H', self.dt)) + fp.write(struct.pack('I', self.headdate)) + fp.write(struct.pack('H', self.head)) + fp.write(b"\xff") # header end + if self.bytesize == 1: + fp.write(bytes(self.buf)) + elif self.bytesize == 2: + for val in self.buf: + fp.write(struct.pack('H', val)) + elif self.bytesize == 3: + for val in self.buf: + fp.write((val >> 16) & 0xff) + fp.write((val >> 8) & 0xff) + fp.write(val & 0xff) + elif self.bytesize == 4: + for val in self.buf: + fp.write(struct.pack('I', val)) + return True + + def checkfile(self): + """ + Check if file header matches buffer metadata + Name is not taken into account because it is optional + """ + with open(self.filename, 'rb') as fp: + header = fp.read(32) + magic = header[:4] + if not (header[:4] == self.magic): + print(f"Invalid magic: {magic}") + return False + bs = header[20] + if not (bs == self.bytesize): + print(f"Invalid bytesize: {bs}") + return False + vc = struct.unpack('H', header[21:23])[0] + if not (vc == self.size): + eprint(f"Invalid value count: {vc}") + return False + ts = struct.unpack('H', header[23:25])[0] + if not (ts == self.dt): + eprint(f"Invalid time step: {ts}") + return False + return True + + def updatefile(self, value): + """ + Write value to file and update header accordingly + """ + pos = 32 + self.head * self.bytesize + self.fp.seek(25) + self.fp.write(struct.pack('IH', self.headdate, self.head + 1)) + self.fp.seek(pos) + if self.bytesize == 1: + self.fp.write(struct.pack('B', value)) + elif self.bytesize == 2: + self.fp.write(struct.pack('H', value)) + elif self.bytesize == 3: + self.fp.write((value >> 16) & 0xff) + self.fp.write((value >> 8) & 0xff) + self.fp.write(value & 0xff) + elif self.bytesize == 4: + self.fp.write(struct.pack('I', value)) + + def clearfile(self): + """ + Clear data part of history file + """ + self.fp.seek(25) + self.fp.write(struct.pack('IH', int(time.time()), 0)) + fp.seek(32) + for p in range(32, self.size * self.bytesize): + fp.write(0) + +class History(): + """ + A history can consist of different time series with different + temporal resolutions + + TODO implement type (e.g. pressure humidity temp etc.) to identify data + """ + def __init__(self, basename, delta_min): + self.delta_t = delta_min # smallest unit of time in the series + self.series = dict() + self.basepath = "/tmp" + self.basename = basename + + def __str__(self): + out = f"History {self.basename} (min. {self.delta_t}s) in {self.basepath}\n" + n = 0 + for ser in self.series.values(): + out += f" Series: {ser.name} {ser.dt}s {ser.filename}\n" + n += 1 + if n == 0: + out += " No series found\n" + return out + + def addSeries(self, name, size, delta_t): + """ + Check whether a series already exists and throw an error if so. + The new delta t must also be divisible by delta_min + """ + if delta_t in self.series: + raise KeyError(f"Error: delta t {delta_t} already exists") + if delta_t < self.delta_t: + raise ValueError(f"Error: delta t {delta_t} too small, minimum is {self.delta_t}") + if delta_t % self.delta_t != 0: + raise ValueError(f"Error: delta t have to be a multiple of {self.delta_t}") + hb = HistoryBuffer(name, size, delta_t) + histfilename = f"hb{self.basename}_{size}-{delta_t}.dat" + hb.filename = os.path.join(self.basepath, histfilename) + self.series[delta_t] = hb + return hb + + def clear(self): + # Clear all time series buffer + self.series.clear() diff --git a/nmea2000/lookup.py b/nmea2000/lookup.py new file mode 100644 index 0000000..a234c98 --- /dev/null +++ b/nmea2000/lookup.py @@ -0,0 +1,563 @@ +# Lookup tables + +accesslevel = { + 0: "Locked", + 1: "unlocked level 1", + 2: "unlocked level 2" +} + +alarmgroup = { + 0: "Instrument", + 1: "Autopilot", + 2: "Radar", + 3: "Chart Plotter", + 4: "AIS" +} + +alarmid = { + 0: "No Alarm", + 1: "Shallow Depth", + 2: "Deep Depth", + 3: "Shallow Anchor", + 4: "Deep Anchor", + 5: "Off Course", + 6: "AWA High", + 7: "AWA Low", + 8: "AWS High", + 9: "AWS Low", + 10: "TWA High", + 11: "TWA Low", + 12: "TWS High", + 13: "TWS Low", + 14: "WP Arrival", + 15: "Boat Speed High", + 16: "Boat Speed Low", + 17: "Sea Temperature High", + 18: "Sea Temperature Low", + 19: "Pilot Watch", + 20: "Pilot Off Course", + 21: "Pilot Wind Shift", + 22: "Pilot Low Battery", + 23: "Pilot Last Minute Of Watch", + 24: "Pilot No NMEA Data", + 25: "Pilot Large XTE", + 26: "Pilot NMEA DataError", + 27: "Pilot CU Disconnected", + 28: "Pilot Auto Release", + 29: "Pilot Way Point Advance", + 30: "Pilot Drive Stopped", + 31: "Pilot Type Unspecified", + 32: "Pilot Calibration Required", + 33: "Pilot Last Heading", + 34: "Pilot No Pilot", + 35: "Pilot Route Complete", + 36: "Pilot Variable Text", + 37: "GPS Failure", + 38: "MOB", + 39: "Seatalk1 Anchor", + 40: "Pilot Swapped Motor Power", + 41: "Pilot Standby Too Fast To Fish", + 42: "Pilot No GPS Fix", + 43: "Pilot No GPS COG", + 44: "Pilot Start Up", + 45: "Pilot Too Slow", + 46: "Pilot No Compass", + 47: "Pilot Rate Gyro Fault", + 48: "Pilot Current Limit", + 49: "Pilot Way Point Advance Port", + 50: "Pilot Way Point Advance Stbd", + 51: "Pilot No Wind Data", + 52: "Pilot No Speed Data", + 53: "Pilot Seatalk Fail1", + 54: "Pilot Seatalk Fail2", + 55: "Pilot Warning Too Fast To Fish", + 56: "Pilot Auto Dockside Fail", + 57: "Pilot Turn Too Fast", + 58: "Pilot No Nav Data", + 59: "Pilot Lost Waypoint Data", + 60: "Pilot EEPROM Corrupt", + 61: "Pilot Rudder Feedback Fail", + 62: "Pilot Autolearn Fail1", + 63: "Pilot Autolearn Fail2", + 64: "Pilot Autolearn Fail3", + 65: "Pilot Autolearn Fail4", + 66: "Pilot Autolearn Fail5", + 67: "Pilot Autolearn Fail6", + 68: "Pilot Warning Cal Required", + 69: "Pilot Warning OffCourse", + 70: "Pilot Warning XTE", + 71: "Pilot Warning Wind Shift", + 72: "Pilot Warning Drive Short", + 73: "Pilot Warning Clutch Short", + 74: "Pilot Warning Solenoid Short", + 75: "Pilot Joystick Fault", + 76: "Pilot No Joystick Data", + 80: "Pilot Invalid Command", + 81: "AIS TX Malfunction", + 82: "AIS Antenna VSWR fault", + 83: "AIS Rx channel 1 malfunction", + 84: "AIS Rx channel 2 malfunction", + 85: "AIS No sensor position in use", + 86: "AIS No valid SOG information", + 87: "AIS No valid COG information", + 88: "AIS 12V alarm", + 89: "AIS 6V alarm", + 90: "AIS Noise threshold exceeded channel A", + 91: "AIS Noise threshold exceeded channel B", + 92: "AIS Transmitter PA fault", + 93: "AIS 3V3 alarm", + 94: "AIS Rx channel 70 malfunction", + 95: "AIS Heading lost/invalid", + 96: "AIS internal GPS lost", + 97: "AIS No sensor position", + 98: "AIS Lock failure", + 99: "AIS Internal GGA timeout", + 100: "AIS Protocol stack restart", + 101: "Pilot No IPS communications", + 102: "Pilot Power-On or Sleep-Switch Reset While Engaged", + 103: "Pilot Unexpected Reset While Engaged", + 104: "AIS Dangerous Target", + 105: "AIS Lost Target", + 106: "AIS Safety Related Message (used to silence)", + 107: "AIS Connection Lost", + 108: "No Fix" +} + +alarmstatus = { + 0: "Alarm condition not met", + 1: "Alarm condition met and not silenced", + 2: "Alarm condition met and silenced" +} + +# Class 1, 2, Level A, B? +certlevel = { # Not yet verified! + 0: "None", + 1: "Certified", + 2: "Not applicable" +} + +control = { + 0: "ACK", + 1: "NAK", + 2: "Access Denied", + 3: "Address Busy" +} + +deviceclass = { + 0: "Reserved for 2000 Use", + 10: "System tools", + 20: "Safety systems", + 25: "Internetwork device", + 30: "Electrical Distribution", + 35: "Electrical Generation", + 40: "Steering and Control surfaces", + 50: "Propulsion", + 60: "Navigation", + 70: "Communication", + 75: "Sensor Communication Interface", + 80: "Instrumentation/general systems", # deprecated + 85: "External Environment", + 90: "Internal Environment", + 100: "Deck + cargo + fishing equipment systems", + 110: "Human Interface", + 120: "Display", + 125: "Entertainment" +} + +devicefunction = { # dependent of deviceclass above + 0: {}, + 10: {130: "Diagnostic", + 140: "Bus Traffic Logger" + }, + 20: {110: "Alarm Enunciator", + 130: "Emergency Positon Indicating Radia Beacon (EPIRB)", + 135: "Man Overboard", + 140: "Voyage Date Recorder", + 150: "Camera" + }, + 25: {130: "PC Gateway", + 131: "NMEA 2000 to Analog Gateway", + 132: "Analog to NMEA 2000 Gateway", + 135: "NMEA 0183 Gateway", + 140: "Router", + 150: "Bridge", + 160: "Repeater" + }, + 30: {130: "Binary Event Monitor", + 140: "Load Controller", + 141: "AC/DC Input", + 150: "Function Controller" + }, + 35: {140: "Engine", + 141: "DC Generator/Alternator", + 142: "Solar Panel (Solar Array)", + 143: "Wind Generator (DC)", + 144: "Fuel Cell", + 145: "Network Power Supply", + 151: "AC Generator", + 152: "AC Bus", + 153: "AC Mains (Utility/Shore)", + 154: "AC Output", + 160: "Power Converter - Battery Charger", + 161: "Power Converter - Battery Charger+Inverter", + 162: "Power Converter - Inverter", + 163: "Power Converter DC", + 170: "Battery", + 180: "Engine Gateway" + }, + 40: {130: "Follow-up Controller", + 140: "Mode Controller", + 150: "Autopilot", + 155: "Rudder", + 160: "Heading Sensors", # deprecated + 170: "Trim (Tabs)/Interceptors", + 180: "Attitude (Pitch, Roll, Yaw) Control" + }, + 50: {130: "Engineroom Monitoring", # deprecated + 140: "Engine", + 141: "DC Generator/Alternator", + 150: "Engine Controller", # deprecated + 151: "AC Generator", + 155: "Motor", + 160: "Engine Gateway", + 165: "Transmission", + 170: "Throttle/Shift Control", + 180: "Actuator", # deprecated + 190: "Gauge Interface", #deprecated + 200: "Gauge Large", # deprecated + 210: "Gauge Small" # deprecated + }, + 60: {130: "Bottom Depth", + 135: "Bottom Depth/Speed", + 140: "Ownship Attitude", + 145: "Ownship Position (GNSS)", + 150: "Ownship Position (Loran C)", + 155: "Speed", + 160: "Turn Rate Indicator", # deprecated + 170: "Integrated Navigaton", # deprecated + 175: "Integrated Navigation System", + 190: "Navigation Management", + 195: "Automatic Identification System (AIS)", + 200: "Radar", + 201: "Infrared Imaging", + 205: "ECDIS", # deprecated + 210: "ECS", # deprecated + 220: "Direction Finder", # deprecated + 230: "Voyage Status" + }, + 70: {130: "EPIRB", # deprecated + 140: "AIS", # deprecated + 150: "DSC", # deprecated + 160: "Data Receiver/Transceiver", + 170: "Satellite", + 180: "Radio-telephone (MF/HF)", # deprecated + 190: "Radiotelephone" + }, + 75: {130: "Temperature", + 140: "Pressure", + 150: "Fluid Level", + 160: "Flow", + 170: "Humidity" + }, + 80: {130: "Time/Date Systems", # deprecated + 140: "VDR", # deprecated + 150: "Integrated Instrumentation", # deprecated + 160: "General Purpose Displays", # deprecated + 170: "General Sensor Box", # deprecated + 180: "Wheather Instruments", # deprecated + 190: "Transducer/General", # deprecated + 200: "NMEA 0183 Converter" # deprecated + }, + 85: {130: "Athmospheric", + 140: "Aquatic" + }, + 90: {130: "HVAC" + }, + 100: {130: "Scale (Catch)" + }, + 110: { # NEW? WIP + }, + 120: {130: "Display", + 140: "Alarm Enunciator" + }, + 125: {130: "Multimedia Player", + 140: "Multimedia Controller" + } +} + +fluidtype = { + 0: "Fuel", + 1: "Water", + 2: "Gray Water", + 3: "Live Well", + 4: "Oil", + 5: "Black Water", + 6: "Fuel Gasoline", + 14: "Error", + 15: "Unavailable" +} + +industrygroup = { + 0: "Global", + 1: "Highway", + 2: "Agriculture", + 3: "Construction", + 4: "Marine", + 5: "Industrial" +} + +manufacturer = { + 69: "ARKS Enterprises, Inc.", + 78: "FW Murphy/Enovation Controls", + 80: "Twin Disc", + 85: "Kohler Power Systems", + 88: "Hemisphere GPS Inc", + 116: "BEP Marine", + 135: "Airmar", + 137: "Maretron", + 140: "Lowrance", + 144: "Mercury Marine", + 147: "Nautibus Electronic GmbH", + 148: "Blue Water Data", + 154: "Westerbeke", + 161: "Offshore Systems (UK) Ltd.", + 163: "Evinrude/BRP", + 165: "CPAC Systems AB", + 168: "Xantrex Technology Inc.", + 172: "Yanmar Marine", + 174: "Volvo Penta", + 175: "Honda Marine", + 176: "Carling Technologies Inc. (Moritz Aerospace)", + 185: "Beede Instruments", + 192: "Floscan Instrument Co. Inc.", + 193: "Nobletec", + 198: "Mystic Valley Communications", + 199: "Actia", + 200: "Honda Marine", + 201: "Disenos Y Technologia", + 211: "Digital Switching Systems", + 215: "Xintex/Atena", + 224: "EMMI NETWORK S.L.", + 225: "Honda Marine", + 228: "ZF", + 229: "Garmin", + 233: "Yacht Monitoring Solutions", + 235: "Sailormade Marine Telemetry/Tetra Technology LTD", + 243: "Eride", + 250: "Honda Marine", + 257: "Honda Motor Company LTD", + 272: "Groco", + 273: "Actisense", + 274: "Amphenol LTW Technology", + 275: "Navico", + 283: "Hamilton Jet", + 285: "Sea Recovery", + 286: "Coelmo SRL Italy", + 295: "BEP Marine", + 304: "Empir Bus", + 305: "NovAtel", + 306: "Sleipner Motor AS", + 307: "MBW Technologies", + 311: "Fischer Panda", + 315: "ICOM", + 328: "Qwerty", + 329: "Dief", + 341: "Böning Automationstechnologie GmbH & Co. KG", + 345: "Korean Maritime University", + 351: "Thrane and Thrane", + 355: "Mastervolt", + 356: "Fischer Panda Generators", + 358: "Victron Energy", + 370: "Rolls Royce Marine", + 373: "Electronic Design", + 374: "Northern Lights", + 378: "Glendinning", + 381: "B & G", + 384: "Rose Point Navigation Systems", + 385: "Johnson Outdoors Marine Electronics Inc Geonav", + 394: "Capi 2", + 396: "Beyond Measure", + 400: "Livorsi Marine", + 404: "ComNav", + 409: "Chetco", + 419: "Fusion Electronics", + 421: "Standard Horizon", + 422: "True Heading AB", + 426: "Egersund Marine Electronics AS", + 427: "em-trak Marine Electronics", + 431: "Tohatsu Co, JP", + 437: "Digital Yacht", + 438: "Comar Systems Limited", + 440: "Cummins", + 443: "VDO (aka Continental-Corporation)", + 451: "Parker Hannifin aka Village Marine Tech", + 459: "Alltek Marine Electronics Corp", + 460: "SAN GIORGIO S.E.I.N", + 466: "Veethree Electronics & Marine", + 467: "Humminbird Marine Electronics", + 470: "SI-TEX Marine Electronics", + 471: "Sea Cross Marine AB", + 475: "GME aka Standard Communications Pty LTD", + 476: "Humminbird Marine Electronics", + 478: "Ocean Sat BV", + 481: "Chetco Digitial Instruments", + 493: "Watcheye", + 499: "Lcj Capteurs", + 502: "Attwood Marine", + 503: "Naviop S.R.L.", + 504: "Vesper Marine Ltd", + 510: "Marinesoft Co. LTD", + 517: "NoLand Engineering", + 518: "Transas USA", + 529: "National Instruments Korea", + 532: "Onwa Marine", + 571: "Marinecraft (South Korea)", + 573: "McMurdo Group aka Orolia LTD", + 578: "Advansea", + 579: "KVH", + 580: "San Jose Technology", + 583: "Yacht Control", + 586: "Suzuki Motor Corporation", + 591: "US Coast Guard", + 595: "Ship Module aka Customware", + 600: "Aquatic AV", + 605: "Aventics GmbH", + 606: "Intellian", + 612: "SamwonIT", + 614: "Arlt Tecnologies", + 637: "Bavaria Yacts", + 641: "Diverse Yacht Services", + 644: "Wema U.S.A dba KUS", + 645: "Garmin", + 658: "Shenzhen Jiuzhou Himunication", + 688: "Rockford Corp", + 704: "JL Audio", + 715: "Autonnic", + 717: "Yacht Devices", + 734: "REAP Systems", + 735: "Au Electronics Group", + 739: "LxNav", + 743: "DaeMyung", + 744: "Woosung", + 773: "Clarion US", + 776: "HMI Systems", + 777: "Ocean Signal", + 778: "Seekeeper", + 781: "Poly Planar", + 785: "Fischer Panda DE", + 795: "Broyda Industries", + 796: "Canadian Automotive", + 797: "Tides Marine", + 798: "Lumishore", + 799: "Still Water Designs and Audio", + 802: "BJ Technologies (Beneteau)", + 803: "Gill Sensors", + 811: "Blue Water Desalination", + 815: "FLIR", + 824: "Undheim Systems", + 838: "TeamSurv", + 844: "Fell Marine", + 847: "Oceanvolt", + 862: "Prospec", + 868: "Data Panel Corp", + 890: "L3 Technologies", + 894: "Rhodan Marine Systems", + 896: "Nexfour Solutions", + 905: "ASA Electronics", + 909: "Marines Co (South Korea)", + 911: "Nautic-on", + 930: "Ecotronix", + 962: "Timbolier Industries", + 963: "TJC Micro", + 968: "Cox Powertrain", + 969: "Blue Seas", + 1850: "Teleflex Marine (SeaStar Solutions)", + 1851: "Raymarine", + 1852: "Navionics", + 1853: "Japan Radio Co", + 1854: "Northstar Technologies", + 1855: "Furuno", + 1856: "Trimble", + 1857: "Simrad", + 1858: "Litton", + 1859: "Kvasar AB", + 1860: "MMP", + 1861: "Vector Cantech", + 1862: "Yamaha Marine", + 1863: "Faria Instruments", + 2001: "Open Boat Projects" +} + +pilotmode = { + 64: "Standby", + 66: "Auto", + 70: "Wind", + 74: "Track" +} + +pressure = { + 0: "Athmospheric", + 1: "Water", + 2: "Steam", + 3: "Compressed Air", + 4: "Hydraulic", + 5: "Filter", + 6: "AltimeterSetting", + 7: "Oil", + 8: "Fuel" +} + +prnusage = { + 0: "Not Tracked", + 1: "Tracked", + 2: "Used", + 3: "Not tracked+Diff", + 4: "Tracked+Diff", + 5: "Used+Diff", + 14: "Error", + 15: "No Selection" +} + +speedwater = { + 0: "Paddle wheel", + 1: "Pitot tube", + 2: "Doppler", + 3: "Correlation (ultra sound)", + 4: "Electro Magnetic" +} + +timesource = { + 0: "GPS", + 1: "GLONASS", + 2: "Radio Station", + 3: "Local Cesium clock", + 4: "Local Rubidium clock", + 5: "Local Crystal clock" +} + +temperature = { + 0: "Sea Temperature", + 1: "Outside Temperature", + 2: "Inside Temperature", + 3: "Engine Room Temperature", + 4: "Main Cabin Temperature", + 5: "Live Well Temperature", + 6: "Bait Well Temperature", + 7: "Refrigeration Temperature", + 8: "Heating System Temperature", + 9: "Dew Point Temperature", + 10: "Apparent Wind Chill Temperature", + 11: "Theoretical Wind Chill Temperature", + 12: "Heat Index Temperature", + 13: "Freezer Temperature", + 14: "Exhaust Gas Temperature", + 15: "Shaft Seal Temperature" +} + +xtemode = { + 0: "auto", + 1: "differential", + 2: "estimated", + 3: "simulation", + 4: "manual" +} diff --git a/nmea2000/mavg.py b/nmea2000/mavg.py new file mode 100644 index 0000000..604be4e --- /dev/null +++ b/nmea2000/mavg.py @@ -0,0 +1,25 @@ +''' +Moving Average + +Sortierung einer Liste nach Alter? FIFO? + +''' + +import time + +class mAvg(): + + def __init__(self, interval): + self.interval = interval + self.avg = None + self.data = [] + + def addVal(self, value): + self.data.append((time.time(), value)) + + def setInterval(self, interval): + pass + + def getAvg(self): + return self.avg + \ No newline at end of file diff --git a/nmea2000/parser.py b/nmea2000/parser.py new file mode 100644 index 0000000..c816918 --- /dev/null +++ b/nmea2000/parser.py @@ -0,0 +1,224 @@ +''' + +PGNs verarbeiten + +''' + +import struct + +def parse_126996(buf, source, device): + # Product information + n2kvers = (buf[0] + buf[1] * 256) / 1000 + prodcode = buf[2] + buf[3] * 256 + modelid = buf[4:36] # 256bit modelid ascii text + softvers = buf[36:68] # 256bit software version code ascii text + modelvers = buf[68:100] # 256bit model version ascii text + serial = buf[100:132] # 256bit model serial code ascii text + # Füllzeichen entfernen. 0x20, 0xff, '@' für AIS + # Die N2K-Bibliothek von ttlappalainen liefert 0xff + modelid = modelid.rstrip(b'\xff') + softvers = softvers.rstrip(b'\xff') + modelvers = modelvers.rstrip(b'\xff') + serial = serial.rstrip(b'\xff') + # Übertragen in die Geräteliste + devices[source].n2kvers = n2kvers + devices[source].productcode = prodcode + devices[source].modelvers = modelvers.decode('ascii').rstrip() + devices[source].softvers = softvers.decode('ascii').rstrip() + devices[source].product = modelid.decode('ascii').rstrip() + devices[source].serial = serial.decode('ascii').rstrip() + devices[source].certlevel = buf[132] + devices[source].loadequiv = buf[133] + +def parse_126998(buf, source, device): + # Configuration information + # Installation Description 1 + txtlen = buf[0] + if txtlen > 2: + device.instdesc1 = buf[2:txtlen].decode('ascii') + p = txtlen + else: + device.instdesc1 = "" + p = 2 + # Installation Description 2 + txtlen = buf[p] + if txtlen > 2: + device.instdesc2 = buf[p+2:p+txtlen].decode('ascii') + p += txtlen + else: + device.instdesc2 = "" + p += 2 + # Manufacturer Info + txtlen = buf[p] + if txtlen > 2: + device.manufinfo = buf[p+2:p+txtlen].decode('ascii') + else: + device.manufinfo = "" + +def parse_127257(buf, boatdata): + # Attitude + sid = buf[0] + yaw = struct.unpack_from('> 4 + boatdata.tank[instance].capacity = struct.unpack_from('> 6 # 0: true, 1: magnetic, 2: error + cog = struct.unpack_from('> 4 + navterm = buf[1] & 0x03 + xte = struct.unpack_from(' self.age_interval: + self.heap[key][0] -= 1 + + def is_empty(self): + return len(self.heap) == 0 diff --git a/nmea2000/receiver.py b/nmea2000/receiver.py new file mode 100644 index 0000000..4108533 --- /dev/null +++ b/nmea2000/receiver.py @@ -0,0 +1,61 @@ +''' + +Momentan nur Idee + +Verarbeiten von Frames + +Nur Empfang von Paketen / Frames +- single +- fast +- transport + +fast packets können parallel von mehreren Quellen verarbeitet werden + +''' + +import math + +class FpReceiver(): + + def __init__(self): + self.sc0 = {} + self.nf = {} + self.frame = {} + self.wip = [] + + def receive(self, data, source) + ''' + Liefert True wenn Paket komplett empfangen wurde + ''' + sc = (data[0] & 0xf0) >> 5 + fc = data[0] & 0x1f + if not source in self.wip: + # Erster Frame + if fc != 0: + # unbekannte Herkunft und kein Startframe + continue + self.sc0[source] = sc + datalen = data[1] + self.nf[source] = math.ceil((datalen - 6) / 7) + 1 # Anzahl Frames + self.frame[source] = {fc : data[2:]} # Ersten Frame merken + else: + # Folgeframes + if (sc == self.sc0[source]): + # TODO prüfe ob der Framecounter fc schon vorgekommen ist + if not fc in self.frame[source]: + self.frame[source][fc] = data[1:8] + else: + # TODO Fehler im Fast-Packet: doppelter fc! + raise('Frame error: duplicate fc') + if len(self.frame[source]) == self.nf[source]: + self.wip.remove(source) + return True + return False + + def getPacket(self, source) + # TODO Frames in der richtigen reihenfolge zusammensetzen + packet = bytearray() + for frame in sorted(self.frame.items()): + print(frame) + #packet.extend() + return packet diff --git a/nmea2000/routing.py b/nmea2000/routing.py new file mode 100644 index 0000000..773fb25 --- /dev/null +++ b/nmea2000/routing.py @@ -0,0 +1,17 @@ +''' +Routen und Wegepunkte +''' +class Waypoint(): + def __init__(self, number, name): + self.number = number + self.name = name + self.lat = None + self.lon = None + +class Route(): + def __init__(self, number, name) + self.number = number + self.name = name + self.wps = dict() + def getActiveWP(self): + return None diff --git a/nmea2000/valdesc.py b/nmea2000/valdesc.py new file mode 100644 index 0000000..fced0e4 --- /dev/null +++ b/nmea2000/valdesc.py @@ -0,0 +1,179 @@ +''' +Lange Beschreibung der Daten, mehrsprachig + +''' +desc = { + "ALT": { + "en": "Altitude", + "de": "Höhe über Grund" + }, + "AWA": { + "en": "Apparant Wind Angle", + "de": "Scheinbare Windrichtung" + }, + "AWS": { + "en": "Apparant Wind Speed", + "de": "Scheinbare Windgeschwindigkeit" + }, + "BTW": { + "en": "Bearing To Waypoint", + "de": "Kurs zum nächsten Wegepunkt" + }, + "COG": { + "en": "Course Over Ground", + "de": "Kurs über Grund" + }, + "DBS": { + "en": "Depth Below Surface", + "de": "Tiefe unter Wasseroberfläche" + }, + "DBT": { + "en": "Depth Below Transducer", + "de": "Tiefe unter Sensor" + }, + "DEV": { + "en": "Deviation", + "de": "Kursabweichung" + }, + "DTW": { + "en": "Distance To Waypoint", + "de": "Entfernung zum nächsten Wegepunkt" + }, + "GPSD": { + "en": "GPS Date", + "de": "GPS-Datum" + }, + "GPST": { + "en": "GPS Time", + "de": "GPS-Zeit" + }, + "HDM": { + "en": "Magnetic Heading", + "de": "Magnetischer Kurs" + }, + "HDT": { + "en": "Heading", + "de": "Wahrer rechtweisender Kurs" + }, + "HDOP": { + "en": "Horizontal Dilation Of Position", + "de": "Positionsgenauigkeit in der Horizontalen" + }, + "LAT": { + "en": "Latitude", + "de": "Geographische Breite" + }, + "LON": { + "en": "Longitude", + "de": "Geographische Länge" + }, + "Log": { + "en": "Logged distance", + "de": "Entfernung" + }, + "MaxAws": { + "en": "Maximum Apperant Wind Speed", + "de": "Maximum der relativen Windgeschwindigkeit" + }, + "MaxTws": { + "en": "Maximum True Wind Speed", + "de": "Maximum der wahren Windgeschwindigkeit" + }, + "PDOP": { + "en": "Position dilation", + "de": "Positionsgenauigkeit im Raum" + }, + "PRPOS": { + "en": "Secondary Rudder Position", + "de": "Auslenkung Sekundärruder" + }, + "ROT": { + "en": "Rotation", + "de": "Drehrate" + }, + "RPOS": { + "en": "Rudder Position", + "de": "Auslenkung Ruder" + }, + "SOG": { + "en": "Speed Over Ground", + "de": "Geschwindigkeit über Grund" + }, + "STW": { + "en": "Speed Through Water", + "de": "Geschwindigkeit durch das Wasser" + }, + "SatInfo": { + "en": "Satellit Info", + "de": "Anzahl der sichtbaren Satelliten" + }, + "TWD": { + "en": "True Wind Direction", + "de": "Wahre Windrichtung" + }, + "TWS": { + "en": "True Wind Speed", + "de": "Wahre Windgeschwindigkeit" + }, + "TZ": { + "en": "Timezone", + "de": "Zeitzone" + }, + "TripLog": { + "en": "Trip Log", + "de": "Tages-Entfernungszähler" + }, + "VAR": { + "en": "Course Variation", + "de": "Abweichung vom Sollkurs" + }, + "VDOP": { + "en": "Vertical Dilation Of Position", + "de": "Positionsgenauigkeit in der Vertikalen" + }, + "WPLat": { + "en": "Waypoint Latitude", + "de": "Geo. Breite des Wegpunktes" + }, + "WPLon": { + "en": "Waypoint Longitude", + "de": "Geo. Länge des Wegpunktes" + }, + "WTemp": { + "en": "Water Temperature", + "de": "Wassertemperatur" + }, + "XTE": { + "en": "Cross Track Error", + "de": "Kursfehler" + }, + + # Sonderwerte + "xdrHum": { + "en": "Humidity", + "de": "Luftfeuchte" + }, + "xdrPress": { + "en": "Pressure", + "de": "Luftdruck" + }, + "xdrRotK": { + "en": "Keel Rotation", + "de": "Kielrotation" + }, + "tdrTemp": { + "en": "Temperature", + "de": "Temperatur" + }, + "xdrVBat": { + "en": "Battery Voltage", + "de": "Batteriespannung" + }, + + # WIP + "xdrCurr": { + "en": "Current", + "de": "Stromstärke" + } + +} diff --git a/obp60.conf b/obp60.conf new file mode 100644 index 0000000..a83c562 --- /dev/null +++ b/obp60.conf @@ -0,0 +1,96 @@ +[system] +systemname = OBP60v +loglevel = 3 +deviceid = 100 +simulation = on +histpath = ~/.local/lib/obp60 + +[bme280] +enabled = true +port = 1 +address = 0x76 + +[gps] +enabled = false +port = /dev/ttyACM0 + +[opencpn] +port = /dev/ttyV1 +navobj = ~/.opencpn/navobj.xml +config = ~/.opencpn/opencpn.conf + +[settings] +timezone = 1 +boat_draft = 1.3 +fuel_tank = 17 +fuel_consumption = 1.5 +water_tank_1 = 47 +water_tank_2 = 50 +battery_voltage = 12 +battery_type = AGM +battery_capacity = 200 +solar_power = 0 +generator_power = 0 + +[units] +length_format = m +distance_format = nm +speed_format = kn +wind_speed_format = ln +temperature_format = C +date_format = ISO + +[display] +hold_values = off +backlight_color = red +flash_led_mode = limit + +[buzzer] +buzzer_gps_error = off +buzzer_gps_fix = off +buzzer_by_limits = off +buzzer_mode = off +buzzer_power = 50 + +[pages] +number_of_pages = 10 +start_page = 1 + +[page1] +type=Voltage + +[page2] +type=Barograph + +[page3] +type=Anchor + +[page4] +type=Autobahn + +[page5] +type=Clock + +[page6] +type=TwoValues +value1=LAT +value2=LON + +[page7] +type=ThreeValues +value1=COG +value2=STW +value3=DBT + +[page8] +type=FourValues +value1=AWA +value2=AWS +value3=COG +value4=STW + +[page9] +type=Rudder + +[page10] +type=SkyView diff --git a/obp60.py b/obp60.py new file mode 100755 index 0000000..2385255 --- /dev/null +++ b/obp60.py @@ -0,0 +1,543 @@ +#!/usr/bin/env python + +''' +Virtuelles Multifunktionsgerät + +Benötigte Pakete: + python3-cairo python3-gi python3-gi-cairo gir1.2-rsvg-2.0 + +Um transparente Darstellung unter Openbox zu erhalten muß xcompmgr +installiert und konfiguriert werden. + +Wenn ein lokaler GPS-Empfänger angeschlossen ist, so kann dieser genutzt +werden. Zusätzlich benötigte Pakete: + python-serial python3-nmea2 + +Für Unterstützung des BME280-Sensors sind die Bibliotheken smbus2 und +bme280 erforderlich. + +Routen und Wegepunkte können von OpenCPN empfangen werden. Dazu muß eine +passende serielle Schnittstelle für NMEA0183-Ausgang definiert werden. +Im System kann diese in der Datei rc.local aktiviert werden: + # Create virtual serial connection + socat pty,rawer,echo=0,group-late=dialout,mode=0660,link=/dev/ttyV0 \ + pty,rawer,echo=0,group-late=dialout,mode=0660,link=/dev/ttyV1 & +OpenCPN sendet dann Datensätze über ttyV0 und dieses Programm +empfängt sie über ttyV1. +Die Wegepunkte werden in OpenCPN im Standard auf 6 Zeichen gekürzt. +Über die Konfigurationsdatei Settings / MaxWaypointNameLength= +ist es möglich einen anderen Wert einzustellen im Bereich zwischen +3 und 32. Lt. NMEA0183 ist die maximale Länge 10 Zeichen. + + +Zeichensätze müssen, sofern sie noch nicht vorhanden sind in +/usr/local/share/fonts abgelegt werden + +Buttonlayout: +[ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] + +Button 6 ist reserviert für Beleuchtung ILUM + +Neu +Button 2: Abbruch +Button 3: zurück +Button 4: hoch +Button 5: Ok/Menu + +Verlagerung der Seitenanzeige in den Titel als Ziffer in Klammern + +Vergleich zum Garmin GMI20: +[ Zurück ] [ Hoch ] [ Menü ] [ Runter ] [ on/off ] + + +Button 1 wird für AVG verwendet auf mehreren Seiten +Button 5 wird für Trend TRND verwendet + +Aber sowas von WIP + + +Änderungsprotokoll +================== + +Version Datum Änderung(en) von +-------- ----------- ------------------------------------------------------ ---- +0.1 2024-10-31 Entwicklung begonnen tho +0.2 2024-12-24 Veräffentlichung als Git-Repository tho + + +''' + +import os +import sys +import configparser +from setproctitle import setproctitle, setthreadtitle +import gi +gi.require_version('Gtk', '3.0') +gi.require_version('Rsvg', '2.0') +from gi.repository import GLib, Gtk, Gdk, Rsvg +import cairo +import math +import threading +import can +import serial +import smbus2 +import pynmea2 +import bme280 +import math +import time +from datetime import datetime +from nmea2000 import Device, BoatData, History, HistoryBuffer +from nmea2000 import parser +import pages +import struct + +__author__ = "Thomas Hooge" +__copyright__ = "Copyleft 2024, all rights reversed" +__version__ = "0.2" +__email__ = "thomas@hoogi.de" +__status__ = "Development" + +cfg = { + 'cfgfile': 'obp60.conf', + 'imgpath': os.path.join(sys.path[0], 'images'), + 'deviceid': 100, + 'gps': False, + 'bme280': False +} + +def rxd_n2k(): + setthreadtitle("N2Klistener") + bus = can.Bus(interface='socketcan', channel='can0', bitrate=250000); + wip = False + sc = 0 + nf = 0 + while not shutdown: + msg = bus.recv(2) + if not msg: + continue + priority = (msg.arbitration_id & 0x1c000000) >> 26 + source = msg.arbitration_id & 0x000000ff + pgn = (msg.arbitration_id & 0x3ffff00) >> 8 + match pgn: + case 129025: + # Position + #lat = struct.unpack_from('> 4 + #instance = msg.data[0] & 0x0f + #level = struct.unpack_from('> 5 + fc = msg.data[0] & 0x1f + if not wip: + if fc != 0: + continue + source0 = source # muß über das Fast-packet konstant bleiben + sc0 = sc # -"- + fc0 = fc # dieser Zähler wird inkrementiert + datalen = msg.data[1] + nf = math.ceil((datalen - 6) / 7) + 1 + buf129540 = msg.data[2:] + wip = True + else: + if (source == source0) and (sc == sc0) and (fc == fc0 + 1): + buf129540.extend(msg.data[1:8]) + fc0 = fc + else: + # Dieser Frame paßt nicht + #print("PGN 129540: sc/fc mismatch") + pass + if fc == nf: + wip = False + parser.parse_129540(buf129540, boatdata) + case _: + pass + bus.shutdown() + +def rxd_0183(devname): + # Prüfe ob Port vorhanden ist und sich öffnen läßt + try: + ser = serial.Serial(devname, 115200, timeout=3) + except serial.SerialException as e: + print("OpenCPN serial port not available") + return + setthreadtitle("0183listener") + while not shutdown: + try: + msg = pynmea2.parse(ser.readline().decode('ascii')) + except pynmea2.nmea.ParseError: + continue + if msg.sentence_type == 'GLL': + boatdata.setValue("LAT", msg.latitude) + boatdata.setValue("LON", msg.longitude) + elif msg.sentence_type == 'VTG': + boatdata.setValue("COG", int(msg.true_track)) + boatdata.setValue("SOG", float(msg.spd_over_grnd_kts[:-1])) + elif msg.sentence_type == 'VHW': + boatdata.setValue("STW", float(msg.water_speed_knots)) + elif msg.sentence_type == 'WPL': + # Wegepunkt + print(msg.fields) + elif msg.sentence_type == 'RTE': + # Route + print(msg.fields) + ser.close() + +def datareader(histpath, history): + """ + Daten zu fest definierten Zeitpunkten lesen + + Die Schleife läuft einmal alle Sekunden immer zum gleichen Zeitpunkt. + Die Nutzlast sollte demnach weniger als eine Sekunde Laufzeit haben. + Falls durch eine außergewöhnliche Situation doch einmal mehr als eine + Sekunde benötigt werden sollte, gleicht sich das in den darauffolgenden + Durchläufen wieder aus. + + """ + setthreadtitle("datareader") + + # Speicherpfad für Meßwertdaten + if not os.path.exists(histpath): + os.makedirs(histpath) + history.basepath = histpath + # Serien initialisieren + history.addSeries("BMP280-75", 336 ,75) + history.addSeries("BMP280-150", 336 , 150) + history.addSeries("BMP280-300", 336 , 300) + history.addSeries("BMP280-600", 336 , 600) + history.addSeries("BMP280-900", 336 , 900) + for s in history.series.values(): + s.begin() + + def g_tick(n=1): + t = time.time() + count = 0 + while True: + count += n + yield max(t + count - time.time(), 0) + g = g_tick(1) + + n = 0 + while not shutdown: + time.sleep(next(g)) + # BME280 abfragen + if cfg['bme280']: + sensordata = bme280.sample(smbus, cfg['bme280_address'], cfg['bme280_cp']) + # Aktuellen Wert merken + boatdata.setValue("xdrTemp", sensordata.temperature) + boatdata.setValue("xdrPress", sensordata.pressure) + boatdata.setValue("xdrHum", sensordata.humidity) + # Historie schreiben + pval = int(sensordata.pressure *10) + for k, v in history.series.items(): + if n % k == 0: + v.add(pval) + # Lokales GPS abfragen + # TODO + if cfg['gps']: + pass + n += 1 + + for s in history.series.values(): + s.finish() + +class Frontend(Gtk.Window): + + button = { + 1: (75, 485), + 2: (150, 492), + 3: (227, 496), + 4: (306, 496), + 5: (382, 492), + 6: (459, 485) + } + radius = 30 + + def __init__(self, device, boatdata, profile): + super().__init__() + self.owndev = device + self.boatdata = boatdata + + self.connect("destroy", self.on_destroy) + + self.set_position(Gtk.WindowPosition.CENTER) + self.set_size_request(530, 555) + self.set_title("OBP60 virt") + self.set_app_paintable(True) + self.set_decorated(False) + self.set_keep_above(True) + + self.screen = self.get_screen() + self.visual = self.screen.get_rgba_visual() + if (self.visual is not None and self.screen.is_composited()): + self.set_visual(self.visual) + + handle = Rsvg.Handle() + self._svg = handle.new_from_file(os.path.join(sys.path[0], "obp60.svg")) + + self.connect("draw", self.on_draw) + + self.da = Gtk.DrawingArea() + self.da.add_events(Gdk.EventMask.BUTTON_PRESS_MASK|Gdk.EventMask.BUTTON_RELEASE_MASK) + self.add(self.da) + self.da.connect("draw", self.da_draw) + self.da.connect('button-press-event', self.da_button_press) + self.da.connect('button-release-event', self.da_button_release) + + self.button_clicked = 0 # Geklickter Button vor Loslassen + self.keylock = False + self.pages = profile + self.pageno = 1 + self.curpage = self.pages[self.pageno] + + print("Wische von 2 nach 1 für Programmende") + + def run(self): + GLib.timeout_add_seconds(2, self.on_timer) + self.show_all() + Gtk.main() + + def on_timer(self): + # Boatdata validator + boatdata.updateValid(5) + # Tastaturstatus an Seite durchreichen + self.curpage.keylock = self.keylock + # Neuzeichnen + self.queue_draw() + return True + + def on_draw(self, widget, ctx): + # Fenstertransparenz + ctx.set_source_rgba(0, 0, 0, 0) + ctx.paint() + + def da_draw(self, widget, ctx): + viewport = Rsvg.Rectangle() + viewport.x = 0 + viewport.y = 0 + viewport.width = 530 + viewport.height = 555 + self._svg.render_document(ctx, viewport) + ctx.set_source_rgb(1.0, 0, 0) + ctx.translate(64, 95) # Koordinatenursprung auf virtuellen Displaybereich setzen + ctx.rectangle(0, 0, 400, 300) + ctx.clip() + + ctx.set_source_rgb(0, 0, 0) # Schwarz auf Weiß + + # Heartbeat umschalten + if self.curpage.header: + self.curpage.draw_header(ctx) + self.curpage.draw(ctx) + if self.curpage.footer: + self.curpage.draw_footer(ctx) + + def da_button_press(self, widget, event): + # Es gibt eine Liste mit Objekten und hier wird + # geprüft ob und in welches Objekt geklickt wurde + # Die eigentliche Funktion wird beim Loslassen ausgelöst. + # Damit sind Wischgesten simulierbar + self.button_clicked = 0 + if (event.x < self.button[1][0] - self.radius or event.x > self.button[6][0] + self.radius): + return True + if (event.y < self.button[1][1] - self.radius or event.y > self.button[3][1] + self.radius): + return True + for b, v in self.button.items(): + diff = math.sqrt((event.x - v[0])**2 + (event.y - v[1])**2) + if diff < self.radius: + self.button_clicked = b + break + return True + + def da_button_release(self, widget, event): + # Hier sind die eigentlichen Tastenfunktionen + # + # Die Auswertung ist abhängig von der jew. angezeigten Seite + # Jede Seite kann eine Methode "handle_key" implementieren + # Falls der Rückgabewert "True" ist, hat die Seite die Taste + # verarbeitet, die Funktion hier wird damit unterdrückt. + # TODO + if (event.x < self.button[1][0] - self.radius or event.x > self.button[6][0] + self.radius): + return True + if (event.y < self.button[1][1] - self.radius or event.y > self.button[3][1] + self.radius): + return True + selected = 0 + for b, v in self.button.items(): + diff = math.sqrt((event.x - v[0])**2 + (event.y - v[1])**2) + if diff < self.radius: + selected = b + break + if self.keylock: + # Bei Tastensperre einzige Möglichkeit: Tastensperre ausschalten + if selected == 6 and self.button_clicked == 1: + self.keylock = False + return True + if selected == 1: + if self.button_clicked == 2: + # Programmende bei Klicken auf 2 und loslassen auf 1 + self.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.WATCH)) + Gtk.main_quit() + elif self.button_clicked == 6: + # Klick auf 6 und loslassen auf 1 ist Tastatursperre + self.keylock = True + else: + self.curpage.handle_key(1) + elif selected == 2: + # Abbruch/Zurück + self.curpage.handle_key(2) + elif selected == 3: + # runter / eine Seite vor + if not self.curpage.handle_key(3): + if self.pageno > 1: + self.pageno -= 1 + else: + self.pageno = len(self.pages) - 1 + self.curpage = self.pages[self.pageno] + elif selected == 4: + if not self.curpage.handle_key(4): + if self.pageno < len(self.pages) - 1: + self.pageno += 1 + else: + self.pageno = 1 + # hoch / eine Seite zurück + self.curpage = self.pages[self.pageno] + elif selected == 5: + # Ok/Menü + self.curpage.handle_key(5) + elif selected == 6: + if self.button_clicked == 6: + # Backlight on/off + self.curpage.backlight = not self.curpage.backlight + elif self.button_clicked == 5: + # Umschalten zur Systemseite + self.curpage = self.pages[0] + return True + + def on_destroy(self, widget): + Gtk.main_quit() + +def init_profile(config, cfg, boatdata): + ''' + config: Configparser-Objekt + cfg: Laufende Programmkonfiguration + Die Liste und Anordnung der Seiten nennen wir "Profil" + Seiten-Profil aus Konfiguration erstellen + Seite Nummer 0 ist immer die Systemseite. Diese ist nicht + über die normale Seitenreihenfolge erreichbar, sondern + durch eine spezielle Tastenkombination/Geste. + TODO Prüfungen einbauen: + Fortlaufende Seitennummern ab 1 + Fortlaufende Wertenummern ab 1 + Maximalwerte nicht überschreiten + ''' + pages_max = config.getint('pages', 'number_of_pages') + + # Suche alle Abschnitte, die mit "page" beginnen + sects = config.sections() + sects.remove('pages') + pagedef = {} + n = 0 + for s in sects: + if s.startswith('page'): + # Nummer und Art ermitteln + pageno = int(s[4:]) + pagedef[pageno] = {'type': config.get(s, "type")} + # Hole ein bin maximal 4 Werte je Seite + values = {} + valno = 1 + for i in (1, 2, 3, 4): + try: + values[i] = config.get(s, f"value{i}") + except configparser.NoOptionError: + break + pagedef[pageno]['values'] = values + n += 1 + if n >= pages_max: + break + clist = { + 0: pages.System(0, cfg, boatdata) + } + for i, p in pagedef.items(): + try: + cls = getattr(pages, p['type']) + except AttributeError: + # Klasse nicht vorhanden, Seite wird nicht benutzt + print(f"Klasse '{type}' nicht gefunden") + continue + c = cls(i, cfg, boatdata, *[v for v in p['values'].values()]) + clist[i] = c + return clist + +if __name__ == "__main__": + + #setproctitle("obp60v") + + shutdown = False + owndevice = Device(100) + boatdata = BoatData() + boatdata.addTank(0) + boatdata.addEngine(0) + + # Basiskonfiguration aus Datei lesen + config = configparser.ConfigParser() + config.read(os.path.join(sys.path[0], cfg['cfgfile'])) + cfg['deviceid'] = config.getint('system', 'deviceid') + cfg['simulation'] = config.getboolean('system', 'simulation') + cfg['histpath'] = os.path.expanduser(config.get('system', 'histpath')) + + cfg['gps'] = config.getboolean('gps', 'enabled') + if cfg['gps']: + cfg['gps_port'] = config.get('gps', 'port') + + cfg['bme280'] = config.getboolean('bme280', 'enabled') + if cfg['bme280']: + cfg['bme280_port'] = config.getint('bme280', 'port') + cfg['bme280_address'] = int(config.get('bme280', 'address'), 16) # convert 0x76 + smbus = smbus2.SMBus(cfg['bme280_port']) + cfg['bme280_cp'] = bme280.load_calibration_params(smbus, cfg['bme280_address']) + history = History("press", 75) + boatdata.addHistory(history, "press") + + cfg['ocpn_port'] = config.get('opencpn', 'port') + + if cfg['simulation']: + boatdata.enableSimulation() + + profile = init_profile(config, cfg, boatdata) + + t_rxd_n2k = threading.Thread(target=rxd_n2k) + t_rxd_n2k.start() + t_rxd_0183 = threading.Thread(target=rxd_0183, args=(cfg['ocpn_port'],)) + t_rxd_0183.start() + + t_data = threading.Thread(target=datareader, args=(cfg['histpath'], history)) + t_data.start() + + app = Frontend(owndevice, boatdata, profile) + app.run() + shutdown = True + t_rxd_n2k.join() + t_rxd_0183.join() + print("Another fine product of the Sirius Cybernetics Corporation.") diff --git a/obp60.svg b/obp60.svg new file mode 100644 index 0000000..7777244 --- /dev/null +++ b/obp60.svg @@ -0,0 +1,106 @@ + + + + diff --git a/pages/__init__.py b/pages/__init__.py new file mode 100644 index 0000000..441eb26 --- /dev/null +++ b/pages/__init__.py @@ -0,0 +1,36 @@ +# Displayseiten + +from .system import System + +# Generische Seiten +from .onevalue import OneValue +from .twovalues import TwoValues +from .threevalues import ThreeValues +from .fourvalues import FourValues +from .fourvalues2 import FourValues2 + +# Graphen +from .onegraph import OneGraph +from .twographs import TwoGraphs +from .exhaust import Exhaust + +# Analoginstrumente +from .clock import Clock +from .fluid import Fluid + +# Spezialseiten +from .anchor import Anchor +from .apparentwind import ApparentWind +from .autobahn import Autobahn +from .barograph import Barograph +from .battery import Battery +from .battery2 import Battery2 +from .bme280 import BME280 +from .dst810 import DST810 +from .keel import Keel +from .rollpitch import RollPitch +from .skyview import SkyView +from .solar import Solar +from .rudder import Rudder +from .voltage import Voltage +from .windrose import WindRose diff --git a/pages/anchor.py b/pages/anchor.py new file mode 100644 index 0000000..68ecea7 --- /dev/null +++ b/pages/anchor.py @@ -0,0 +1,52 @@ +""" + +Ankerinfo / -alarm + +""" + +import os +import cairo +import math +from .page import Page + +class Anchor(Page): + + def __init__(self, pageno, cfg, boatdata): + super().__init__(pageno, cfg, boatdata) + self.sym_anchor = cairo.ImageSurface.create_from_png(os.path.join(cfg['imgpath'], "anchor.png")) + self.buttonlabel[1] = 'DEC' + self.buttonlabel[2] = 'INC' + self.buttonlabel[5] = 'SET' + + def draw(self, ctx): + + # Name + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(20) + + ctx.move_to(2, 50) + ctx.show_text("Anchor") + ctx.move_to(320, 50) + ctx.show_text("Chain") + + ctx.set_font_size(16) + ctx.move_to(2, 70) + ctx.show_text("Alarm: off") + ctx.move_to(320, 70) + ctx.show_text("45 m") + ctx.stroke() + + + # Spezialseite + cx = 200 + cy = 150 + r = 125 + + ctx.set_line_width(1.5) + ctx.arc(cx, cy, r, 0, 2*math.pi) + ctx.stroke() + + ctx.save() + ctx.set_source_surface(self.sym_anchor, cx-8, cy-8) + ctx.paint() + ctx.restore() diff --git a/pages/apparentwind.py b/pages/apparentwind.py new file mode 100644 index 0000000..881ea55 --- /dev/null +++ b/pages/apparentwind.py @@ -0,0 +1,16 @@ +import cairo +import math +from .page import Page + +class ApparentWind(Page): + + def __init__(self, pageno, cfg, boatdata): + super().__init__(pageno, cfg, boatdata) + + def draw(self, ctx): + # Name + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(60) + ctx.move_to(20, 100) + ctx.show_text("Apparent Wind") + diff --git a/pages/autobahn.py b/pages/autobahn.py new file mode 100644 index 0000000..a144946 --- /dev/null +++ b/pages/autobahn.py @@ -0,0 +1,102 @@ +""" + +3D-View angelehnt an die NASA Clipper GPS-Darstellung + +""" + +import os +import cairo +import math +from .page import Page + +class Autobahn(Page): + + def __init__(self, pageno, cfg, boatdata): + super().__init__(pageno, cfg, boatdata) + self.xte = self.bd.getRef("XTE") + self.cog = self.bd.getRef("COG") + self.btw = self.bd.getRef("BTW") + self.dtw = self.bd.getRef("DTW") + self.wpname = "no data" + self.symbol = cairo.ImageSurface.create_from_png(os.path.join(cfg['imgpath'], "ship.png")) + + def draw(self, ctx): + # Beschriftung unter den Werten + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(16) + ctx.move_to(50, 188); + ctx.show_text("Cross-track error") + ctx.move_to(270, 188) + ctx.show_text("Track") + ctx.move_to(45, 275); + ctx.show_text("Distance to waypoint") + ctx.move_to(260, 275); + ctx.show_text("Bearing") + ctx.stroke() + + # Meßwerte + ctx.select_font_face("DSEG7 Classic") + ctx.set_font_size(60) + + ctx.move_to(40, 170) + #ctx.show_text(self.xte.format()) + ctx.show_text("2.3") + ctx.move_to(220, 170) + #ctx.show_text(self.cog.format()) + ctx.show_text("253") + ctx.move_to(40, 257) + #ctx.show_text(self.dtw.format()) + ctx.show_text("5.8") + ctx.move_to(220, 257) + #ctx.show_text(self.btw.format()) + ctx.show_text("248") + + # 3D-Ansicht oben + # TODO Schiffssymbol + ctx.save() + ctx.set_source_surface(self.symbol, 186, 68) + ctx.paint() + ctx.restore() + + # Segmente: 2 1 0 3 4 5 + seg = [True] * 6 + points = { + 2: ((0, 54), (46, 24), (75, 24), (0, 90)), + 1: ((0, 100), (82, 24), (112, 24), (50, 100)), + 0: ((60, 100), (117, 24), (147, 24), (110, 100)), + 3: ((340, 100), (283, 24), (253, 24), (290, 100)), + 4: ((399, 100), (318, 24), (289, 24), (350, 100)), + 5: ((399, 54), (354, 24), (325, 24), (399, 90)) + } + + # Winkeldifferenz + diff = (self.cog.value or 0) - (self.btw.value or 0) + if diff < -180: + diff += 360 + elif diff > 180: + diff -= 360 + + if diff > 0: + order = (3, 4, 5, 0, 1, 2) + else: + order = (0, 1, 2, 3, 4, 5) + + # Anzahl aktiver Segmente + seg_step = math.radians(3) + nseg = min(abs(diff) / seg_step, 5) + i = 0 + while nseg > 0: + seg[order[i]] = False + i += 1 + nseg -= 1 + + # Segmente zeichnen + for p in range(6): + ctx.move_to(*points[p][0]) + ctx.line_to(*points[p][1]) + ctx.line_to(*points[p][2]) + ctx.line_to(*points[p][3]) + if seg[p]: + ctx.fill() + else: + ctx.stroke() diff --git a/pages/barograph.py b/pages/barograph.py new file mode 100644 index 0000000..6b50434 --- /dev/null +++ b/pages/barograph.py @@ -0,0 +1,366 @@ +""" + +Siehe auch: Steamrock Digital Barometer + +Meßwert alls 15 Minuten: + Es wird in hPa gemessen mit einer Nachkommastelle + 84 Stunden * 4 Werte je Stunde = 336 Meßwerte +Tendenzwert über 3 Stunden + +Je Zoomstufe wird ein eigener Buffer vorgehalten um ein sauberes +Diagramm zu erhalten. Überall gilt: Je Pixel ein Meßwert. + +Drucktendenz: + - 1 hour tendency + - 3 hour tendency + +Verschiedene Datenquellen auswählbar: +- intern (BME280, BMP280) +- N2K generisch +- Historie von + - Yacht devices + - Capteurs? + +Das Diagramm wird mit Ursprung rechts unten (x0, y0) gezeichnet, +da die Werte in der Vergangenhait liegen, also links vom Ursprung. + +Damit eine saubere Skala auf der Y-Achse erreicht wird, gibt einige +feste Skalierungen. +Standard: 20hPa von unten nach oben, z.B. 1015, 1020, 1025, 1030, 1035 + +""" + +import time +import cairo +from .page import Page + +class Barograph(Page): + + def __init__(self, pageno, cfg, boatdata): + super().__init__(pageno, cfg, boatdata) + # Meßwert alle 15 Minuten: + # 84 Stunden * 4 Werte je Stunde = 336 Meßwerte + self.bd = boatdata + self.source = 'I' # (I)ntern, e(X)tern + self.zoom = (1, 2, 3, 6, 12) + self.zoomindex = 4 + self.series = (75, 150, 300, 600, 900) + + # Y-Axis + self.vmin = 0 + self.vmax = 0 + self.scalemin = 1000 + self.scalemax = 1020 + self.scalestep = 5 + + # Tendenzwert über 3 Stunden + self.hist3 = None + self.hist1 = None + + self.buttonlabel[1] = '+' + self.buttonlabel[2] = '-' + self.buttonlabel[5] = 'SRC' + + self.refresh = time.time() - 30 + + def handle_key(self, buttonid): + # TODO Serie auswählen aufgrund Zoomlevel + if buttonid == 1: + # Zoom in + if self.zoomindex > 0: + self.zoomindex -= 1 + self.refresh = time.time() - 30 + elif buttonid == 2: + # Zoom out + if self.zoomindex < len(self.zoom) - 1: + self.zoomindex += 1 + self.refresh = time.time() - 30 + if buttonid == 5: + # Source + if self.source == 'I': + self.source = 'X' + else: + self.source = 'I' + + # Testausgabe der Datenerfassung + data = [] + vmin = data[0] + vmax = data[0] + i = self.series[self.zoomindex] + for value in self.bd.history['press'].series[i].get(): + v = value / 10 + data.append(v) + if v < vmin and v != 0: + vmin = v + elif v > vmax and v != 0: + vmax = v + print(f"Werte: vmin={vmin}, vmax={vmax}") + ymin, ymax, step = self.getYScale(vmin, vmax) + print(f"Skala: ymin={ymin}, ymax={ymax}, step={step}") + print(f"zoomindex={self.zoomindex}, series={self.series[self.zoomindex]}") + + hist1a = self.bd.history['press'].series[i].getvalue(3600) + hist1b = self.bd.history['press'].series[i].getvalue3(3600) + trend1 = data[0] - hist1b + print(f"{hist1a} / {hist1b} -> Trend1: {trend1:.1f}") + + def loadData(self): + """ + Transfer data from history to page buffer + set y-axis according to data + """ + self.data = [] + self.vmin = 9999 + self.vmax = 0 + i = self.series[self.zoomindex] + for value in self.bd.history['press'].series[i].get(): + v = value / 10 + self.data.append(v) + if v < self.vmin and v != 0: + self.vmin = v + elif v > self.vmax and v != 0: + self.vmax = v + self.scalemin, self.scalemax, self.scalestep = self.getYScale(self.vmin, self.vmax) + return True + + def drawTrend(self, ctx, code, x, y, w): + """ + One hour Trend + 0: Stationary <= 1 hPa + 1: Rising >1 and <= 2 hPa + 2: Rising fast >2 and <= 3 hPa + 3: Rising very fast >3 hPa + -1: Falling + -2: Falling fast + -3: Falling very fast + """ + trend1map = { + -3: "Falling_Very_Fast.png", # > 3 hPa + -2: "Falling_Fast.png", # > 2 and <= 3 hPa + -1: "Falling.png", # > 1 and <= 2 hPa + 0: "Stationary.png", # <= +/- 1 hPa + 1: "Rising.png", # < -1 and >= -2 hPa + 2: "Rising_Fast.png", # < -2 and >= -3 hPa + 3: "Rising_Very_Fast.png" # < -3 hPa + } + + if code == 0: + # Pfeil horizontal rechts + ctx.move_to(x, y - w / 2) + ctx.line_to(x + w, y - w / 2) + ctx.draw() + # Position merken + ctx.line_to(x - w / 4, y - w) + ctx.line_to(x - w / 4, y) + ctx.line_to(x + w, y - w / 2) + ctx.fill() + elif code == 1: + # Pfeil schräg nach oben + pass + elif code == 2: + # Pfeil gerade nach oben + pass + elif code == 3: + # Doppelpfeil nach oben + pass + elif code == -1: + # Pfeil schräg nach unten + pass + elif code == -2: + # Pfeil gerade nach unten + pass + elif code == -3: + # Doppelpfeil nach unten + pass + + def drawWMOCode(self, ctx, code, x, y, w): + """ + Three hour code + Code 0 to 8: + 0: Increasing, then decreasing; athmospheric pressure the same + as or higher than three hours ago + 1: Increasing then steady; or increasing, then increasing more + slowly; athmospheric pressure now higher than three hours ago + 2: Increasing (steadily or unsteadily); athmospheric pressure + now higher than three hours ago + 3: Decreasing or steady, then increasing; or increasing then + increasing more rapidly; athmospheric pressure now higher + than three hours ago + 4: Steady; athmospheric pressure is the same as three hours ago + 5: Decreasing, then increasing; athmospheric pressure now is the + same as or lower than three hours ago + 6: + 7: + 8: + """ + pass + + def getYScale(self, vmin, vmax): + # Y-Achse aufgrund Meßwerten einstellen + diff = vmax - vmin + if diff < 20: + step = 5 + elif diff <= 40: + step = 10 + else: + step = 15 + vmin = int(vmin - (vmin % step)) # Nächstes Vielfaches nach oben + vmax = int(vmax + step - (vmax % step)) # Nächstes Vielfaches nach unten + return (vmin, vmax, step) + + def draw(self, ctx): + """ + Darstellung angelehnt an klassisches Gerät + Daten werden im nichtflüchtigen Speicher gehalten + Da sich die Daten langsam verändern, reicht es, diese z.B. nur alle + 30 Sekunden oder langsamer zu laden. + Der aktuelle Wert oben ist natürlich nicht alt. + + Datenreihen + - 1 Woche, stündlich: 7 * 24 = 168 Meßwerte + - 1 Tag, alle 10 min: 24 * 6 = 144 Meßwerte + Der Druck wird in zwei Bytes abgespeichert. Es wird eine Nachkommastelle + verwendet. Um ohne Fließkommazahlen auszukommen wird der Maßwert einfach + mit 10 multipliziert. + + Darstellung wie Steamrock: + 1 Pixel entspricht einem Meßwert alle 15min. + 1 Tag hat dementsprechend eine Breite von 48px + """ + + timestamp = time.time() + if timestamp - self.refresh >= 30: + self.refresh = timestamp + self.loadData() + + ctx.set_source_rgb(0, 0, 0) + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + + # Datenquelle rechts oben + ctx.set_font_size(16) + ctx.move_to(330, 50) + if self.source == 'I': + ctx.show_text("BMP280") + else: + ctx.show_text("N2K Bus") + ctx.stroke() + # Zoomstufe + datastep = self.series[self.zoomindex] + if datastep > 120: + if datastep % 60 == 0: + fmt = "{:.0f} min" + else: + fmt = "{:.1f} min" + datastep /= 60 + else: + fmt = '{} s' + self.draw_text_center(ctx, 360, 62, fmt.format(datastep)) + + # Aktueller Luftdruck hPa + ctx.set_font_size(32) + self.draw_text_center(ctx, 200, 40, self.bd.pressure.format()) + #self.draw_text_center(ctx, 200, 40, "1019.2") + ctx.set_font_size(16) + self.draw_text_center(ctx, 200, 62, "hPa") + + # Trend + ctx.set_font_size(16) + # TODO Trend linie + #trend = + self.draw_text_center(ctx, 295, 62, "0.0") + + # min/max + ctx.move_to(10, 38) + ctx.show_text(f"min: {self.vmin}") + ctx.move_to(10, 50) + ctx.show_text(f"max: {self.vmax}") + + # Alarm + self.draw_text_center(ctx, 70, 62, "Alarm Off") + + # Hintergrundrahmen + ctx.set_line_width(2) + + ctx.move_to(0, 75) + ctx.line_to(400, 75) + + ctx.move_to(130, 20) + ctx.line_to(130, 75) + + ctx.move_to(270, 20) + ctx.line_to(270, 75) + + ctx.move_to(325, 20) + ctx.line_to(325, 75) + + ctx.stroke() + + + # Diagramm + # -------- + ymin = self.scalemin + ymax = self.scalemax + yn = self.scalestep + ystep = (ymax - ymin) / yn + + xstep = 48 + + # Ursprung ist rechts unten + x0 = 350 + y0 = 270 + w = 7 * 48 + h = 180 + + ctx.set_line_width(1) + ctx.rectangle(x0 - w + 0.5, y0 - h + 0.5, w, h) + ctx.stroke() + + # X-Achse sind Stunden + xn = 0 + for xt in [x * -1 * self.zoom[self.zoomindex] for x in range(1,7)]: + xn += 1 + ctx.move_to(x0 - xn * xstep + 0.5, y0) + ctx.line_to(x0 - xn * xstep + 0.5, y0 - h) + ctx.stroke() + self.draw_text_center(ctx, x0 - xn * xstep + 0.5, y0 - 8, str(xt), fill=True) + ctx.stroke() + #for x in (1, 2, 3, 4, 5, 6): + # ctx.move_to(x0 - x * 48 + 0.5, y0 + 0.5) + # ctx.line_to(x0 - x * 48 + 0.5, y0 - h + 0.5) + #ctx.stroke() + + # Y-Achse + ctx.move_to(x0 + 5.5, y0 + 0.5) + ctx.line_to(x0 + 5.5, y0 - h) + ctx.move_to(x0 - w - 5.5, y0 + 0.5) + ctx.line_to(x0 - w - 5.5, y0 -h ) + ctx.stroke() + + dy = 9 # Pixel je hPa + ysmin = self.scalemin + ysmax = self.scalemax + + y = y0 + 0.5 + ystep = self.scalestep + ys = ysmin + while y >= y0 - h: + if ys % ystep == 0: + ctx.move_to(x0 + 10, y + 5.5) + ctx.show_text(str(ys)) + ctx.move_to(x0 - w - 5, y) + ctx.line_to(x0 + 5, y) + else: + ctx.move_to(x0, y) + ctx.line_to(x0 + 5, y) + ctx.move_to(x0 - w - 5, y) + ctx.line_to(x0 - w, y) + y -= dy + ys += 1 + ctx.stroke() + + # Meßdaten + for v in self.data: + x0 -= 1 + if v > 0: + ctx.rectangle(x0, y0 - (v - ysmin) * dy, 1.5, 1.5) + ctx.fill() diff --git a/pages/battery.py b/pages/battery.py new file mode 100644 index 0000000..75df190 --- /dev/null +++ b/pages/battery.py @@ -0,0 +1,66 @@ +""" + +Batteriewerte eines INA219 oder INA226 Sensors +Ähnlich ThreeValue + +""" + +import cairo +from .page import Page + +class Battery(Page): + + avg = (1, 10, 60, 300); + + def __init__(self, pageno, cfg, boatdata): + super().__init__(pageno, cfg, boatdata) + self.avgindex = 0 + self.buttonlabel[1] = 'AVG' + + def handle_key(self, buttonid): + if buttonid == 1: + if self.avgindex < len(self.avg) -1: + self.avgindex += 1 + else: + self.avgindex = 0 + return True + return False + + def draw(self, ctx): + + # Aufteilung in 3 Bereiche durch 2 Linien + ctx.rectangle(0, 105, 400, 3); + ctx.rectangle(0, 195, 400, 3); + ctx.fill() + + # Name + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(40) + ctx.move_to(20, 55) + ctx.show_text("VBat") + ctx.move_to(20, 145) + ctx.show_text("IBat") + ctx.move_to(20, 235) + ctx.show_text("PBat") + ctx.stroke() + + # Einheit + ctx.set_font_size(24) + ctx.move_to(20, 90) + ctx.show_text("V") + ctx.move_to(20, 180) + ctx.show_text("A") + ctx.move_to(20, 270) + ctx.show_text("W") + + + # Werte + ctx.select_font_face("DSEG7 Classic") + ctx.set_font_size(60) + ctx.move_to(180, 90) + ctx.show_text("12.3") + ctx.move_to(180, 180) + ctx.show_text("3.2") + ctx.move_to(180, 270) + ctx.show_text("39.4") + diff --git a/pages/battery2.py b/pages/battery2.py new file mode 100644 index 0000000..56f507e --- /dev/null +++ b/pages/battery2.py @@ -0,0 +1,90 @@ +""" + +Komplexe Batterieübersichtsseite + +""" + +import cairo +from .page import Page + +class Battery2(Page): + + def draw_battery(self, ctx, x, y, w, h, level): + ''' + Das Rechteck ist das komplett umschließende + Level ist der prozentuale Füllstand + ''' + pass + + ''' + // Battery graphic with fill level + void batteryGraphic(uint x, uint y, float percent, int pcolor, int bcolor){ + // Show battery + int xb = x; // X position + int yb = y; // Y position + int t = 4; // Line thickness + // Percent limits + if(percent < 0){ + percent = 0; + } + if(percent > 99){ + percent = 99; + } + // Battery corpus 100x80 with fill level + int level = int((100.0 - percent) * (80-(2*t)) / 100.0); + getdisplay().fillRect(xb, yb, 100, 80, pcolor); + if(percent < 99){ + getdisplay().fillRect(xb+t, yb+t, 100-(2*t), level, bcolor); + } + // Plus pol 20x15 + int xp = xb + 20; + int yp = yb - 15 + t; + getdisplay().fillRect(xp, yp, 20, 15, pcolor); + getdisplay().fillRect(xp+t, yp+t, 20-(2*t), 15-(2*t), bcolor); + // Minus pol 20x15 + int xm = xb + 60; + int ym = yb -15 + t; + getdisplay().fillRect(xm, ym, 20, 15, pcolor); + getdisplay().fillRect(xm+t, ym+t, 20-(2*t), 15-(2*t), bcolor); + ''' + + + def draw(self, ctx): + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(40) + ctx.move_to(10, 65) + ctx.show_text("Bat.") + + # Batterietyp + ctx.move_to(90, 65) + ctx.show_text("AGM") + + # Kapazität + ctx.move_to(10, 200) + ctx.select_font_face("DSEG7 Classic") + ctx.set_font_size(40) + ctx.show_text("12") + + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(16) + ctx.show_text("Ah") + + ctx.move_to(10, 235) + ctx.show_text("Installed") + + ctx.move_to(10, 255) + ctx.show_text("Battery Type") + + # Batteriegraphik + # Rechteck mit Füllstand 100x80, oben zwei Pole + ctx.rectangle(150, 100, 100, 80) + ctx.stroke() + # Füllstand + # Pole + + + ''' + ctx.line_to(2.5, 1.5) + + ctx.set_line_width(0.06) + ''' diff --git a/pages/bme280.py b/pages/bme280.py new file mode 100644 index 0000000..79a3a94 --- /dev/null +++ b/pages/bme280.py @@ -0,0 +1,55 @@ +""" + +Werte eines lokal angeschlossenen BME280/BMP280 + +""" + +import cairo +from .page import Page + +class BME280(Page): + + def __init__(self, pageno, cfg, boatdata): + super().__init__(pageno, cfg, boatdata) + #self.ref1 = self.bd.getRef(boatvalue1) + #self.ref2 = self.bd.getRef(boatvalue2) + #self.ref3 = self.bd.getRef(boatvalue3) + + def draw(self, ctx): + + # Bildschirmunterteilung mit Linien + ctx.rectangle(0, 105, 399, 3) + ctx.rectangle(0, 195, 399, 3) + ctx.fill() + + # Beschriftung + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(40) + # Titel + ctx.move_to(20,55) + ctx.show_text("Temp") + ctx.move_to(20,145) + ctx.show_text("Humid") + ctx.move_to(20, 235) + ctx.show_text("Press") + # Einheit + ctx.set_font_size(24) + ctx.move_to(20, 90) + ctx.show_text("Deg C") + ctx.move_to(20, 180) + ctx.show_text("%") + ctx.move_to(20, 270) + ctx.show_text("hPa") + + # Meßwerte + ctx.select_font_face("DSEG7 Classic") + ctx.set_font_size(60) + # Temperatur °C + ctx.move_to(180, 90) + ctx.show_text("{:.1f}".format(self.bd.temp_air)) + # Feuchte % + ctx.move_to(180, 180) + ctx.show_text("{}".format(int(self.bd.humidity))) + # Luftdruck hPa + ctx.move_to(180, 270) + ctx.show_text("{}".format(int(self.bd.pressure))) diff --git a/pages/clock.py b/pages/clock.py new file mode 100644 index 0000000..e25144b --- /dev/null +++ b/pages/clock.py @@ -0,0 +1,188 @@ +""" + +Uhr + +TODO: Zeitzone anzeigen. Abhängig von Lat, Lon + + Es sollen verschiedene Modi unterstützt werden + - Analoguhr + - Digitaluhr + - Regattauhr / -timer + +""" + +import cairo +import math +from .page import Page +from datetime import datetime +import astral + +class Clock(Page): + + def __init__(self, pageno, cfg, boatdata): + super().__init__(pageno, cfg, boatdata) + self.buttonlabel[1] = 'MODE' + self.buttonlabel[2] = 'TZ' + self.mode = ('A', 'D', 'T') # (A)nalog (D)igital (T)imer + self.modeindex = 1 + self.utc = True + self.location = astral.Location(('Norderstedt', 'Germany', 53.710105, 10.0574378, 'UTC')) + self.location.astral = astral.Astral() + + def handle_key(self, buttonid): + if buttonid == 1: + if self.modeindex < len(self.mode): + self.modeindex += 1 + else: + self.modeindex = 0 + return True + if buttonid == 2: + self.utc = not self.utc + return True + return False + + def draw(self, ctx): + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + + # akuellen Modus anzeigen + if mode[modeindex] == 'A': + self.draw_analog(ctx) + if mode[modeindex] == 'D': + self.draw_digital(ctx) + else: + self.draw_timer(ctx) + + def draw_digital(self, ctx): + ctx.set_font_size(24) + ctx.move_to(10, 220) + ctx.show_text("Digital clock") + + def draw_timer(self, ctx): + ctx.set_font_size(24) + ctx.move_to(10, 220) + ctx.show_text("Timer") + + def draw_analog(self, ctx): + + ts = datetime.now() + sunrise = self.location.sunrise(ts) + sunset = self.location.sunset(ts) + #print(sunrise) + #print(sunset) + + # Datum und Uhrzeit + # Sonnenaufgang + # Sonnenuntergang + + # Wochentag + # ts.strftime('%a') + + # Werte in den Ecken der Uhr + ctx.set_font_size(24) + ctx.move_to(10, 220) + ctx.show_text("Time") + ctx.move_to(10, 95) + ctx.show_text("Date") + ctx.move_to(335, 95) + ctx.show_text("SunR") + ctx.move_to(335, 220) + ctx.show_text("SunS") + ctx.stroke() + + ctx.set_font_size(16) + + ctx.move_to(10, 65) + ctx.show_text(ts.strftime("%d.%m.%Y")) + ctx.move_to(10, 250) + ctx.show_text(ts.strftime("%H:%M")) + ctx.move_to(335, 65) + ctx.show_text(sunrise.strftime("%H:%M")) + ctx.move_to(335, 250) + ctx.show_text(sunset.strftime("%H:%M")) + ctx.stroke() + + # Horizontal separators + ctx.rectangle(0, 149, 60, 3) + ctx.rectangle(340, 149, 60, 3) + ctx.fill() + + # Uhr + cx = 200 + cy = 150 + r = 110 + ctx.arc(cx, cy, r + 10, 0, 2*math.pi) + ctx.arc(cx, cy, r + 7, 0, 2*math.pi) + ctx.stroke() + + ctx.set_font_size(20) + self.draw_text_center(ctx, cx, cy-40, 'UTC' if self.utc else 'LOT') + + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(24) + + for i in range(360): + x = cx + (r - 30) * math.sin(i/180*math.pi) + y = cy - (r - 30) * math.cos(i/180*math.pi) + + char = "" + if i == 0: + char = "12" + elif i == 90: + char = "3" + elif i == 180: + char = "6" + elif i == 270: + char = "9" + + if i % 90 == 0: + #ctx.move_to(x, y) + self.draw_text_center(ctx, x, y, char) + #ctx.stroke() + + ctx.set_line_width(3.0) + if i % 6 == 0: + if i % 30 == 0: + x0 = cx + (r - 10) * math.sin(i/180*math.pi) + y0 = cy - (r - 10) * math.cos(i/180*math.pi) + x1 = cx + (r + 10) * math.sin(i/180*math.pi) + y1 = cy - (r + 10) * math.cos(i/180*math.pi) + ctx.move_to(x0, y0) + ctx.line_to(x1, y1) + ctx.stroke() + else: + x = cx + r * math.sin(i/180*math.pi) + y = cy - r * math.cos(i/180*math.pi) + ctx.arc(x, y, 2, 0, 2*math.pi) + ctx.fill() + + # Stundenzeiger + p = ((cx - 2, cy - (r - 50)), (cx + 2, cy - (r - 50)), (cx + 6, cy + 16), (cx - 6, cy + 16)) + angle_h = (ts.hour % 12 + ts.minute / 60) * 30 + zeiger = self.rotate((cx, cy), p, angle_h) + ctx.move_to(*zeiger[0]) + for point in zeiger[1:]: + ctx.line_to(*point) + ctx.fill() + + # Minutenzeiger + p = ((cx - 1, cy - (r - 15)), (cx + 1, cy - (r - 15)), (cx + 6, cy + 20), (cx - 6, cy + 20)) + angle_m = ts.minute * 6 + zeiger = self.rotate((cx, cy), p, angle_m) + ctx.move_to(*zeiger[0]) + for point in zeiger[1:]: + ctx.line_to(*point) + ctx.fill() + + # Zentraler Kreis + ctx.set_source_rgb(0, 0, 0) + ctx.arc(cx, cy, 12, 0, 2*math.pi) + ctx.fill() + + # Wozu dieses? + ctx.set_source_rgb(0.86, 0.86, 0.86) + ctx.arc(cx, cy, 10, 0, 2*math.pi) + ctx.fill() + + ctx.set_source_rgb(0, 0, 0) + ctx.arc(cx, cy, 2, 0, 2*math.pi) + ctx.fill() diff --git a/pages/dst810.py b/pages/dst810.py new file mode 100644 index 0000000..42803a0 --- /dev/null +++ b/pages/dst810.py @@ -0,0 +1,40 @@ +import cairo +from .page import Page + +class DST810(Page): + + # DBT, STW, Log, WTemp + + def draw(self, ctx): + + # Layout + ctx.rectangle(0, 105, 400, 3) + ctx.rectangle(0, 195, 400, 3) + ctx.rectangle(200, 195, 3, 75) + ctx.fill() + + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(40) + + # titel + ctx.move_to(20, 55) + ctx.show_text("Depth") + ctx.move_to(20, 145) + ctx.show_text("Speed") + ctx.move_to(20, 220) + ctx.show_text("Log") + ctx.move_to(220, 220) + ctx.show_text("Temp") + + # Einheiten + ctx.set_font_size(24) + ctx.move_to(20, 90) + ctx.show_text("m") + + # Meßwerte + ctx.select_font_face("DSEG7 Classic") + ctx.set_font_size(60) + + ctx.move_to(180, 90) + ctx.show_text("m") + ctx.set_font_size(40) diff --git a/pages/exhaust.py b/pages/exhaust.py new file mode 100644 index 0000000..70be7b8 --- /dev/null +++ b/pages/exhaust.py @@ -0,0 +1,58 @@ +""" + +XY-Graphik der Abgastemperatur + +""" + +import cairo +import math +from .page import Page + +class Exhaust(Page): + + def draw(self, ctx): + # Title + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(24) + ctx.move_to(10, 45) + ctx.show_text("Exhaust Temperature") + + # Graph anzeigen X/Y + + x0 = 55 + y0 = 255 + w = 300 + h = 200 + + # X-Achse + ctx.move_to(x0 - 20, y0) + ctx.line_to(x0 + w, y0) + # Y-Achse + ctx.move_to(x0, y0 + 20) + ctx.line_to(x0, y0 - h) + ctx.stroke() + # Pfeispitze X + ctx.move_to(x0-4, y0 - h + 12) + ctx.line_to(x0, y0 - h) + ctx.line_to(x0 + 4, y0 - h + 12) + ctx.fill() + # Pfeilspitze Y + ctx.move_to(x0 + w -12, y0 - 4) + ctx.line_to(x0 + w, y0) + ctx.line_to(x0 + w - 12, y0 + 4) + ctx.fill() + + # Achsenbeschriftung + ctx.set_font_size(16) + ctx.move_to(x0 - 30, y0 - h + 20) + ctx.show_text("°C") + ctx.move_to(x0 + w - 10, y0 + 15) + ctx.show_text("min") + + # Hier wird eine Reihe von Meßwerten erwartet + # Aufgrund min und max kann die Y-Achse skaliert werden + # Die X-Achse ist die Zeit + self.draw_text_center(ctx, x0 - 30, y0 - h / 2, "Temperature", True, False) + + # Einzeichnen von zwei Warnschwellen als horizontale + # Linie (gestrichelt) diff --git a/pages/fluid.py b/pages/fluid.py new file mode 100644 index 0000000..5cdcd6e --- /dev/null +++ b/pages/fluid.py @@ -0,0 +1,137 @@ +""" + +Füllstandsanzeige Tank + + 0: "Fuel", + 1: "Water", + 2: "Gray Water", + 3: "Live Well", + 4: "Oil", + 5: "Black Water", + 6: "Fuel Gasoline", + 14: "Error", + 15: "Unavailable" + +""" + +import os +import cairo +import math +from .page import Page +import nmea2000.lookup + +class Fluid(Page): + + def __init__(self, pageno, cfg, boatdata, fluidtype): + super().__init__(pageno, cfg, boatdata) + self.fluidtype = int(fluidtype) + if self.fluidtype == 0: + self.symbol = cairo.ImageSurface.create_from_png(os.path.join(cfg['imgpath'], "fuelpump.png")) + else: + self.symbol = None + + def draw(self, ctx): + + # Zentrum Instrument + cx = 200 + cy = 150 + # Radius + r = 110 + + # Füllstand von 0 - 100% + # 0 = -120°, 100 = +120° + level = self.bd.tank[0].volume or 0 + angle = -120 + level * 2.4 + + # Rahmen + ctx.set_source_rgb(*self.fgcolor) + ctx.set_line_width(3) + ctx.arc(cx, cy, r, 0, 2*math.pi) + ctx.stroke() + + # Symbol, sofern vorhanden + if self.symbol: + ctx.save() + ctx.set_source_surface(self.symbol, cx - 8, cy - 50) + ctx.paint() + ctx.restore() + + # Fluidtype + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(24) + ctx.move_to(20, 60) + ctx.show_text(nmea2000.lookup.fluidtype[self.fluidtype]) + ctx.stroke() + + # Zeigerrahmen im Zentrum + ctx.arc(cx, cy, 8, 0, 2*math.pi) + ctx.stroke() + + # Zeiger in Nullposition + # Variante 1, einseitig + #p = ((cx - 1, cy - (r - 20)), (cx + 1, cy - (r - 20)), (cx + 4, cy), (cx - 4, cy)) + # Variante 2, überstehend + p = ((cx - 1, cy - (r - 20)), (cx + 1, cy - (r - 20)), (cx + 6, cy + 15), (cx - 6, cy + 15)) + # Zeiger für aktuellen Meßwert + zeiger = self.rotate((cx, cy), p, angle) + + # Zeiger zeichnen + ctx.move_to(*zeiger[0]) + for point in zeiger[1:]: + ctx.line_to(*point) + ctx.fill() + + # Lösche das Zentrum heraus + ctx.set_source_rgb(*self.bgcolor) + ctx.arc(cx, cy, 6, 0, 2*math.pi) + ctx.fill() + + ctx.set_source_rgb(*self.fgcolor) + + # Simple Skala direkt zeichnen + # 50%-Wert oben in der Mitte + + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(16) + + ctx.move_to(cx, cy -r) + ctx.line_to(cx, cy -r + 16) + # linker Anschlag 0% + self.draw_text_center(ctx, cx, cy - r + 30, "1/2") + l = self.rotate((cx, cy), ((cx, cy - r + 16), (cx, cy - r)), -120) + ctx.move_to(*l[0]) + ctx.line_to(*l[1]) + # rechter Anschlag 100% + l = self.rotate((cx, cy), ((cx, cy - r + 16), (cx, cy - r)), 120) + ctx.move_to(*l[0]) + ctx.line_to(*l[1]) + + # 25% + l = self.rotate((cx, cy), ((cx, cy - r + 16), (cx, cy - r)), -60) + ctx.move_to(*l[0]) + ctx.line_to(*l[1]) + tx, ty = self.rotate((cx, cy), ((cx, cy - r + 30),), -60)[0] + self.draw_text_center(ctx, tx, ty, "1/4") + # 75% + l = self.rotate((cx, cy), ((cx, cy - r + 16), (cx, cy - r)), 60) + ctx.move_to(*l[0]) + ctx.line_to(*l[1]) + tx, ty = self.rotate((cx, cy), ((cx, cy - r + 30),), 60)[0] + self.draw_text_center(ctx, tx, ty, "3/4") + + ctx.set_font_size(24) + tx, ty = self.rotate((cx, cy), ((cx, cy - r + 30),), -130)[0] + self.draw_text_center(ctx, tx, ty, "E") + tx, ty = self.rotate((cx, cy), ((cx, cy - r + 30),), 130)[0] + self.draw_text_center(ctx, tx, ty, "F") + ctx.stroke() + + self.draw_text_center(ctx, cx, cy + r - 20, f"{level:.0f}%") + ctx.stroke() + + # Skalenpunkte + # Alle 5% ein Punkt aber nur da wo noch kein Strich ist + for angle in [x for x in range(-120, 120, 12) if x not in (-120, -60, 0, 60, 120)]: + x, y = self.rotate((cx, cy), ((cx, cy - r + 10),), angle)[0] + ctx.arc(x, y, 2, 0, 2*math.pi) + ctx.fill() diff --git a/pages/fourvalues.py b/pages/fourvalues.py new file mode 100644 index 0000000..2d85a9a --- /dev/null +++ b/pages/fourvalues.py @@ -0,0 +1,75 @@ +""" + +Vier frei wählbare Meßwerte + +Layout ++--------------------+ +| 1 | ++--------------------+ +| 2 | ++--------------------+ +| 3 | ++--------------------+ +| 4 | ++--------------------+ + +""" + +import cairo +from .page import Page + +class FourValues(Page): + + def __init__(self, pageno, cfg, boatdata, boatvalue1, boatvalue2, boatvalue3, boatvalue4): + super().__init__(pageno, cfg, boatdata) + self.value1 = boatvalue1 + self.value2 = boatvalue2 + self.value3 = boatvalue3 + self.value4 = boatvalue4 + + def draw(self, ctx): + # Seitenunterteilung + ctx.rectangle(0, 80, 400, 3) + ctx.rectangle(0, 146, 400, 3) + ctx.rectangle(0, 214, 400, 3) + ctx.fill() + + # + + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(32) + ctx.move_to(20, 45) + ctx.show_text("AWA") + ctx.move_to(20, 113) + ctx.show_text("AWS") + ctx.move_to(20, 181) + ctx.show_text("COG") + ctx.move_to(20, 249) + ctx.show_text("STW") + ctx.stroke() + + # Units + ctx.set_font_size(16) + ctx.move_to(20, 65) + ctx.show_text("Deg") + ctx.move_to(20, 133) + ctx.show_text("kn") + ctx.move_to(20, 201) + ctx.show_text("Deg") + ctx.move_to(20, 269) + ctx.show_text("kn") + ctx.stroke() + + # Meßwerte + ctx.select_font_face("DSEG7 Classic") + ctx.set_font_size(40) + + ctx.move_to(180, 65) + ctx.show_text("150") + ctx.move_to(180, 133) + ctx.show_text("25.3") + ctx.move_to(180, 201) + ctx.show_text("146") + ctx.move_to(180, 269) + ctx.show_text("56.4") + diff --git a/pages/fourvalues2.py b/pages/fourvalues2.py new file mode 100644 index 0000000..14ff400 --- /dev/null +++ b/pages/fourvalues2.py @@ -0,0 +1,77 @@ +""" + +Vier frei auswählbare Meßwerte + +Layout ++--------------------+ +| 1 | ++--------------------+ +| 2 | ++--------------------+ +| 3 | 4 | ++--------------------+ + +""" + +import cairo +from .page import Page + +class FourValues2(Page): + + def __init__(self, pageno, cfg, boatdata, boatvalue1, boatvalue2, boatvalue3, boatvalue4): + super().__init__(pageno, cfg, boatdata) + self.value1 = boatvalue1 + self.value2 = boatvalue2 + self.value3 = boatvalue3 + self.value4 = boatvalue4 + + def draw(self, ctx): + + # Seitenunterteilung + ctx.rectangle(0, 105, 400, 3) + ctx.rectangle(0, 195, 400, 3) + ctx.rectangle(200, 195, 3, 75) + ctx.fill() + + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + + # Titel + ctx.set_font_size(40) + ctx.move_to(20, 55) + ctx.show_text("AWA") + ctx.move_to(20, 145) + ctx.show_text("AWS") + + ctx.set_font_size(24) + ctx.move_to(20, 220) + ctx.show_text("COG") + ctx.move_to(220, 220) + ctx.show_text("STW") + + # Einheiten + ctx.set_font_size(16) + ctx.move_to(20, 90) + ctx.show_text("Deg") + ctx.move_to(20, 180) + ctx.show_text("kn") + + ctx.set_font_size(16) + ctx.move_to(20, 240) + ctx.show_text("Deg") + ctx.move_to(220, 240) + ctx.show_text("kn") + + # Meßwerte + ctx.select_font_face("DSEG7 Classic") + ctx.set_font_size(60) + ctx.move_to(180, 90) + ctx.show_text("150") + ctx.move_to(180, 180) + ctx.show_text("33.0") + + ctx.set_font_size(40) + ctx.move_to(80, 270) + ctx.show_text("146") + ctx.move_to(280, 270) + ctx.show_text("50.5") + ctx.stroke() diff --git a/pages/generator.py b/pages/generator.py new file mode 100644 index 0000000..0a92593 --- /dev/null +++ b/pages/generator.py @@ -0,0 +1,44 @@ +""" +WIP Mangels Generator keine Überprüfung möglich +Dies ist im Prinzip ein Platzhalter + +""" + +import cairo +from .page import Page +import math + +class Generator(Page): + + def draw_generator(self, ctx, x, y, r): + ctx.set_line_width(4.0) + ctx.arc(x, y, r) + ctx.set_font_size(60) + self.draw_text_center(ctx, x, y, "G") + + def draw(self, ctx): + # Name + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(40) + ctx.move_to(10, 65) + ctx.show_text("Power") + ctx.move_to(12, 82) + ctx.show_text("Generator") + + # Voltage type + ctx.select_font_face("DSEG7 Classic") + ctx.move_to(10, 140) + # 12 or 24 + ctx.show_text("12V") + + # Generator power + # kW or W + + # Show load level in percent + + # Show sensor type info + # INA219, INA226 + + # Current, A + + # Consumption, W diff --git a/pages/keel.py b/pages/keel.py new file mode 100644 index 0000000..e2aa630 --- /dev/null +++ b/pages/keel.py @@ -0,0 +1,114 @@ +""" + +Rotationssensor AS5600 mit Funktion "Kiel" +WIP + +Idee: + - Zusätzlich Anzeigemöglichkeit für die Tiefe eines variablen Kiels + - Mode-Taste + +""" + +import cairo +import math +from .page import Page + +class Keel(Page): + + def __init__(self, pageno, cfg, boatdata): + super().__init__(pageno, cfg, boatdata) + # Wert für Kielrotation + self.valref = self.bd.getRef("xdrRotK") + + def draw(self, ctx): + + # Mitte oben Instrument (Halbkreis) + cx = 200 + cy = 150 + + # Radius Kielposition + r = 110 + + # Titel + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(32) + ctx.move_to(100, 70) + ctx.show_text("Keel Position") + ctx.set_font_size(24) + ctx.move_to(175, 110) + ctx.show_text(self.valref.unit) + ctx.stroke() + + + # Halbkreis für Skala + ctx.set_source_rgb(*self.fgcolor) + ctx.set_line_width(3) + ctx.arc(cx, cy, r + 10, 0, math.pi) + ctx.stroke() + + # Skala mit Strichen, Punkten und Beschriftung + char = { + 90: "45", + 120: "30", + 150: "15", + 180: "0", + 210: "15", + 240: "30", + 270: "45" + } + # Zeichnen in 10°-Schritten + ctx.set_font_size(16) + for i in range(90, 271, 10): + fx = math.sin(i / 180 * math.pi) + fy = math.cos(i / 180 * math.pi) + if i in char: + x = cx + (r - 30) * fx + y = cy - (r - 30) * fy + self.draw_text_center(ctx, x, y, char[i]) + ctx.stroke() + if i % 30 == 0: + ctx.move_to(cx + (r - 10) * fx, cy - (r - 10) * fy) + ctx.line_to(cx + (r + 10) * fx, cy - (r + 10) * fy) + ctx.stroke() + else: + x = cx + r * fx + y = cy - r * fy + ctx.arc(x, y, 2, 0, 2*math.pi) + ctx.fill() + + # Boot und Wasserlinie + ctx.arc(cx, cy - 10, 28, 0, math.pi) + ctx.fill() + ctx.set_line_width(4) + ctx.move_to(150, cy) + ctx.line_to(250, cy) + ctx.stroke() + + #ctx.arc(200, 150, r + 10, 0, 2*math.pi) + #ctx.fill() + #ctx.set_source_rgb(*self.bgcolor) + #ctx.arc(200, 150, r + 7, 0, 2* math.pi) + #ctx.rectangle(0, 30, 299, 122) + #ctx.fill() + + angle = -15 + #angle = self.valref.value + #TODO Angle limits to +/-45° + if angle < -45: + angle = -45 + elif angle > 45: + angle = 45 + angle *= 2 # stretched scale + + # Kiel + p = ((cx - 6, cy), (cx + 6, cy), (cx + 2, cy + r - 50), (cx - 2, cy + r - 50)) + keel = self.rotate((cx, cy), p, angle) + ctx.move_to(*keel[0]) + for point in keel[1:]: + ctx.line_to(*point) + ctx.fill() + + # Kiel-Bombe + x, y = self.rotate((cx, cy), ((cx, cy + r -50),), angle)[0] + ctx.arc(x, y, 5, 0, 2*math.pi) + ctx.fill() diff --git a/pages/onegraph.py b/pages/onegraph.py new file mode 100644 index 0000000..2be2e84 --- /dev/null +++ b/pages/onegraph.py @@ -0,0 +1,26 @@ +""" + +Frei auswählbaren Meßwert als Graphen anzeigen + + +YDGS01 + History Request PGN 61184 + History Data PGN 130816 + + + +""" + +import cairo +from .page import Page + +class OneGraph(Page): + + def draw(self, ctx): + # Name + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(60) + ctx.move_to(20, 100) + ctx.show_text("One Graph") + + # Graph anzeigen X/Y diff --git a/pages/onevalue.py b/pages/onevalue.py new file mode 100644 index 0000000..3846060 --- /dev/null +++ b/pages/onevalue.py @@ -0,0 +1,31 @@ +import cairo +from .page import Page + +class OneValue(Page): + + def __init__(self, pageno, cfg, boatdata, boatvalue): + super().__init__(pageno, cfg, boatdata) + self.ref1 = self.bd.getRef(boatvalue) + + def draw(self, ctx): + + # Bezeichnung + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(60) + ctx.move_to(20, 100) + ctx.show_text(self.ref1.valname) + + # Einheit + ctx.set_font_size(40) + ctx.move_to(270, 100) + ctx.show_text(self.ref1.unit) + + # Meßwert + ctx.select_font_face("DSEG7 Classic") + ctx.set_font_size(100) + ctx.move_to(40, 240) + if self.ref1.value: + ctx.show_text(self.ref1.format()) + else: + ctx.show_text(self.placeholder) + ctx.stroke() diff --git a/pages/page.py b/pages/page.py new file mode 100644 index 0000000..d7b8dfe --- /dev/null +++ b/pages/page.py @@ -0,0 +1,202 @@ +""" + +Basisklasse für alle Darstellungsseiten + +Hinweise zu Cairo: +Das Koordinatensystem geht von (0, 0) links oben bis (400, 300) rechts unten. +Um exakte Pixel zu treffen müssen Koordinaten mit Offset 0.5 verwendet werden. + +""" +import os +import cairo +import math +from datetime import datetime + +class Page(): + + pageno = 1 # Nummer der aktuell sichtbaren Seite + backlight = False + color_normal = "dcdcdc" # Standardhintergrund + color_lighted = "d89090" # Hintergrund im Nachtmodus + + bgcolor = (0.86, 0.86, 0.86) + fgcolor = (0, 0, 0) + + @staticmethod + def hexcolor(hexstr): + if (len(hexstr) != 6) or (not all(c.lower in '0123456789abcdef' for c in hexstr)): + raise ValueError('Not a valid RGB Hexstring') + else: + return(int(hexstr[0:2], 16) / 255.0, + int(hexstr[2:4], 16) / 255.0, + int(hexstr[4:6], 16) / 255.0) + + @staticmethod + def rotate (origin, points, angle): + # operates on tuples, angle in degrees + ox, oy = origin + phi = math.radians(angle) + fs = math.sin(phi) + fc = math.cos(phi) + rotated = [] + for x, y in points: + dx = x - ox + dy = y - oy + rotated.append((ox + fc * dx - fs * dy, oy + fs * dx + fc * dy)) + return rotated + + def __init__(self, pageno, cfg, boatdata): + self.pageno = pageno + self.cfg = cfg + self.bd = boatdata + self.header = True + self.footer = True + self.hbled = False # Heartbeat LED + self.hbfreq = 1000 # Heartbeat Frequenz in ms + self.keylock = False + self.icon = {} + self.icon['PREV'] = cairo.ImageSurface.create_from_png(os.path.join(cfg['imgpath'], "arrow_l1.png")) + self.icon['NEXT'] = cairo.ImageSurface.create_from_png(os.path.join(cfg['imgpath'], "arrow_r1.png")) + self.icon['ILUM'] = cairo.ImageSurface.create_from_png(os.path.join(cfg['imgpath'], "lighton.png")) + self.sym_lock = cairo.ImageSurface.create_from_png(os.path.join(cfg['imgpath'], "lock.png")) + self.sym_swipe = cairo.ImageSurface.create_from_png(os.path.join(cfg['imgpath'], "swipe.png")) + self.buttonlabel = { + 1: '', + 2: '', + 3: '#PREV', + 4: '#NEXT', + 5: '', + 6: '#ILUM' + } + + def handle_key(self, buttonid): + """ + Diese Methode sollte in der Detailseite überladen werden + """ + print(f"Button no. {buttonid} ignored") + return False + + def heartbeat(self, ctx): + """ + Wie ausschalten bei Seitenwechsel? + """ + ctx.save() + if self.hbled: + ctx.set_source_rgb(0, 0, 0) + else: + ctx.set_source_rgb(0.86, 0.86, 0.86) # 0xdcdcdc + ctx.arc(210, 9, 6, 0, math.pi*2) + ctx.fill() + ctx.restore() + self.hbled = not self.hbled + + def draw_header(self, ctx): + """ + Mögliche Zeichen für aktivierte Funktionen + AP - Accesspoint ist aktiv + WIFI - WIFI-Client + TCP + N2K - NMEA2000 + 183 + USB + GPS - GPS Fix vorhanden + # TODO Umstellung auf Symbole je 16 Pixel zum Platz sparen + Neu: Nummer der aktiven Seite (1 - 10) + """ + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(16) + ctx.move_to(0.5, 14.5) + ctx.show_text(f"N2K GPS") + ctx.stroke() + + # Seitennummer neue Darstellung + ctx.set_line_width(1) + ctx.move_to(170.5, 1.5) + ctx.line_to(190.5, 1.5) + ctx.line_to(190.5, 16.5) + ctx.line_to(170.5, 16.5) + ctx.line_to(170.5, 1.5) + ctx.stroke() + self.draw_text_center(ctx, 180, 9.5, str(self.pageno)) + + # Tastenstatus + ctx.save() + if self.keylock: + ctx.set_source_surface(self.sym_lock, 150, 1) + else: + ctx.set_source_surface(self.sym_swipe, 150, 1) + ctx.paint() + ctx.restore() + + # Heartbeat + self.heartbeat(ctx) + + # Datum und Uhrzeit + ctx.move_to(230, 14.5) + ctx.show_text(datetime.today().strftime('%H:%M %Y-%m-%d LOT')) + ctx.stroke() + + def draw_footer(self, ctx): + """ + Nur Belegung der Buttons (label[1] bis label[6]) + """ + ctx.select_font_face("AtariST8x16SystemFont") + #ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(16) + x = (35, 101, 167, 233, 299, 365) + y = 294 + for i in range(6): + if len(self.buttonlabel[i+1]) > 0 : + if self.buttonlabel[i+1][0] == "#": + # Symbol verwenden 16x16 Pixel + ctx.save() + key = self.buttonlabel[i+1][1:] + ctx.set_source_surface(self.icon[key], x[i]-8, y-13) + ctx.paint() + ctx.restore() + else: + text = "[ {} ]".format(self.buttonlabel[i+1]) + w = ctx.text_extents(text).width + ctx.move_to(x[i] - w/2, y) + ctx.show_text(text) + ctx.stroke() + + def clear(self): + ctx.set_source_rgb(1, 1, 1) + ctx.rectangle(0, 0, 399, 299) + ctx.fill() + ctx.set_source_rgb(0, 0, 0) + + def draw_text_center(self, ctx, x, y, content, rotate=False, baseline=False, fill=False): + ext = ctx.text_extents(content) + if fill: + ctx.set_source_rgb(*self.bgcolor) + xf = x + ext.x_bearing - 2 + yf = y + ext.height / 2 + ext.y_bearing - 2 + wf = ext.width + 4 + hf = ext.height + 4 + ctx.rectangle(xf, yf, wf, hf) + ctx.fill() + ctx.set_source_rgb(*self.fgcolor) + if rotate: + if baseline: + ctx.move_to(x - ext[3] / 2.0, y) + else: + ctx.move_to(x - ext[3] / 2.0, y + ext[2] / 2.0) + ctx.save() + ctx.rotate(1.5 * math.pi) + ctx.show_text(content) + ctx.restore() + else: + if baseline: + ctx.move_to(x - ext[2] / 2.0, y) + else: + ctx.move_to(x - ext[2] / 2.0, y + ext[3] / 2.0) + ctx.show_text(content) + ctx.stroke() + + def draw_text_ralign(self, ctx, x, y, content): + ext = ctx.text_extents(content) + ctx.move_to(x - ext[2], y) + ctx.show_text(content) + ctx.stroke() diff --git a/pages/rollpitch.py b/pages/rollpitch.py new file mode 100644 index 0000000..d9fac18 --- /dev/null +++ b/pages/rollpitch.py @@ -0,0 +1,13 @@ +import cairo +from .page import Page + +class RollPitch(Page): + + def draw(self, ctx): + # Name + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(60) + ctx.move_to(20, 100) + ctx.show_text("RollPitch") + + # Graph anzeigen X/Y diff --git a/pages/rudder.py b/pages/rudder.py new file mode 100644 index 0000000..409b7e5 --- /dev/null +++ b/pages/rudder.py @@ -0,0 +1,103 @@ +import cairo +import math +from .page import Page + +class Rudder(Page): + + def __init__(self, pageno, cfg, boatdata): + super().__init__(pageno, cfg, boatdata) + self.buttonlabel[1] = 'MODE' + self.mode = 'P' + # Werte für Ruderausschlag + self.valpri = self.bd.getRef("RPOS") # Primäres Ruder + self.valsec = self.bd.getRef("PRPOS") # Sekundäres Ruder + + def handle_key(self, buttonid): + if buttonid == 1: + if self.mode == 'P': + self.mode = 'S' + else: + self.mode = 'P' + return True + return False + + def draw(self, ctx): + + # Ruder auswählen + if self.mode == 'P': + valref = self.valpri + else: + valref = self.valsec + + # Rotationszentrum + cx = 200 + cy = 150 + + # Radius des Instruments + r = 110 + + # Titel + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(32) + ctx.move_to(80, 70) + ctx.show_text("Rudder Position") + + ctx.set_font_size(24) + if valref.valid: + ctx.move_to(175, 110) + ctx.show_text(valref.unit) + else: + ctx.move_to(110, 110) + ctx.show_text("no data available") + ctx.stroke() + + # Debug + angle = 5 + + # Rahmen + ctx.set_source_rgb(*self.fgcolor) + ctx.set_line_width(3) + ctx.arc(cx, cy, r + 10, 0, math.pi) + ctx.stroke() + + # Zentrum + ctx.arc(cx, cy, 8, 0, 2*math.pi) + ctx.fill() + + # Skala mit Strichen, Punkten und Beschriftung + char = { + 90: "45", + 120: "30", + 150: "15", + 180: "0", + 210: "15", + 240: "30", + 270: "45" + } + # Zeichnen in 10°-Schritten + ctx.set_font_size(16) + for i in range(90, 271, 10): + fx = math.sin(i / 180 * math.pi) + fy = math.cos(i / 180 * math.pi) + if i in char: + x = cx + (r - 30) * fx + y = cy - (r - 30) * fy + self.draw_text_center(ctx, x, y, char[i]) + ctx.stroke() + if i % 30 == 0: + ctx.move_to(cx + (r - 10) * fx, cy - (r - 10) * fy) + ctx.line_to(cx + (r + 10) * fx, cy - (r + 10) * fy) + ctx.stroke() + else: + x = cx + r * fx + y = cy - r * fy + ctx.arc(x, y, 2, 0, 2*math.pi) + ctx.fill() + + if valref.valid: + p = ((cx - 6, cy), (cx + 6, cy), (cx + 2, cy + r - 50), (cx - 2, cy + r - 50)) + rudder = self.rotate((cx, cy), p, angle) + ctx.move_to(*rudder[0]) + for point in rudder[1:]: + ctx.line_to(*point) + ctx.fill() diff --git a/pages/skyview.py b/pages/skyview.py new file mode 100644 index 0000000..610ada6 --- /dev/null +++ b/pages/skyview.py @@ -0,0 +1,85 @@ +""" + +Satelliteninfos + + - Sat mit Fix: ausgefüllter Kreis + - Sat ohne fix: leerer Kreis + - Slots für 12 SNR-Balken + +""" + +import cairo +import math +from .page import Page + +class SkyView(Page): + + def __init__(self, pageno, cfg, boatdata): + super().__init__(pageno, cfg, boatdata) + + def pol2cart(azimuth, elevation): + ''' + Polar to Cartesian coordinates within the horizon circle. + azimuth in radians + x = math.sin(azimuth) * elevation * self.radius + y = math.cos(azimuth) * elevation * self.radius + ''' + pass + # (x, y) = self.pol2cart(sat.az, sat.el) + + def draw(self, ctx): + + # Name + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + #ctx.set_font_size(32) + #self.draw_text_center(ctx, 200, 40, "Satellite Info") + + # Spezialseite + cx = 130 + cy = 150 + r = 125 + r1 = r / 2 + + ctx.set_line_width(1.5) + ctx.arc(cx, cy, r, 0, 2*math.pi) + ctx.stroke() + ctx.arc(cx, cy, r1, 0, 2*math.pi) + ctx.stroke() + ctx.set_dash([4, 4], 0) + ctx.move_to(cx, cy - r) + ctx.line_to(cx, cy + r) + ctx.move_to(cx - r, cy) + ctx.line_to(cx + r, cy) + ctx.stroke() + ctx.set_dash([], 0) + + # Signal/Noise Balken + ctx.set_font_size(16) + ctx.move_to(325, 34) + ctx.show_text("SNR") + ctx.stroke() + + ctx.set_line_width(1.5) + ctx.rectangle(270, 20, 125, 257) + + # Beispieldaten + ctx.set_line_width(0.5) + for s in range(12): + y = 30 + (s + 1) * 20 + ctx.move_to(275, y) + ctx.show_text(f"{s:02d}") + ctx.rectangle(305, y-12, 85, 14) + ctx.stroke() + + ctx.set_line_width(1.0) + for s in self.bd.sat.values(): + x = cx + math.sin(s.azimuth) * s.elevation * r + y = cy + math.cos(s.azimuth) * s.elevation * r + ctx.arc(x, y, 4, 0, 2*math.pi) + ctx.move_to(x+4, y+4) + ctx.show_text(f"{s.prn_num}") + ctx.stroke() + + # Satellitenliste mit SNR-Balken sortiert nach nummer + for prn_num in sorted(self.bd.sat): + print(prn_num) \ No newline at end of file diff --git a/pages/solar.py b/pages/solar.py new file mode 100644 index 0000000..a384654 --- /dev/null +++ b/pages/solar.py @@ -0,0 +1,40 @@ +import cairo +from .page import Page + +class Solar(Page): + + + def draw_solar(self, ctx, x, y, w, h): + pass + """ + // Solar graphic with fill level + void solarGraphic(uint x, uint y, int pcolor, int bcolor){ + // Show solar modul + int xb = x; // X position + int yb = y; // Y position + int t = 4; // Line thickness + int percent = 0; + // Solar corpus 100x80 + int level = int((100.0 - percent) * (80-(2*t)) / 100.0); + getdisplay().fillRect(xb, yb, 100, 80, pcolor); + if(percent < 99){ + getdisplay().fillRect(xb+t, yb+t, 100-(2*t), level, bcolor); + } + // Draw horizontel lines + getdisplay().fillRect(xb, yb+28-t, 100, t, pcolor); + getdisplay().fillRect(xb, yb+54-t, 100, t, pcolor); + // Draw vertical lines + getdisplay().fillRect(xb+19+t, yb, t, 80, pcolor); + getdisplay().fillRect(xb+39+2*t, yb, t, 80, pcolor); + getdisplay().fillRect(xb+59+3*t, yb, t, 80, pcolor); + + } + """ + + def draw(self, ctx): + # Name + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(60) + ctx.move_to(20, 100) + ctx.show_text("Solar") + diff --git a/pages/system.py b/pages/system.py new file mode 100644 index 0000000..89398d5 --- /dev/null +++ b/pages/system.py @@ -0,0 +1,107 @@ +import cairo +from .page import Page +import datetime + +class System(Page): + + def __init__(self, pageno, cfg, boatdata): + super().__init__(pageno, cfg, boatdata) + self.buttonlabel[1] = 'MODE' + self.buttonlabel[2] = 'STBY' + self.mode = ('I', 'N') # (I)nformation (N)MEA2000 Device List + self.modeindex = 1 + self.standby = False + + def handle_key(self, buttonid): + if self.standby and buttonid != 1: + return True + if buttonid == 1: + if self.standby: + self.standby = False + self.buttonlabel[1] = 'MODE' + self.buttonlabel[2] = 'STBY' + self.header = True + self.footer = True + else: + if self.modeindex < len(self.mode): + self.modeindex += 1 + else: + self.modeindex = 0 + return True + if buttonid == 2: + self.buttonlabel[1] = None + self.buttonlabel[2] = None + self.header = False + self.footer = False + self.standby = True + return False + + def draw_stby(self, ctx): + # Standby + # TODO Kopf und Fußzeile ausschalten + # Ein Klick auf die Mode-Taste weckt das System wieder auf + pass + + def draw_info(self, ctx): + + # Bezeichnung + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(32) + self.draw_text_center(ctx, 200, 40 , "System Info") + + ctx.set_font_size(16) + + # System name + # Software version + + # Linke Seite + ctx.move_to(2, 80) + ctx.show_text("Simulation: ") + ctx.move_to(140, 80) + ctx.show_text('On' if self.cfg['simulation'] else 'Off') + + ctx.move_to(2, 100) + ctx.show_text("BME280/BMP280: ") + ctx.move_to(140, 100) + ctx.show_text('On' if self.cfg['bme280'] else 'Off') + + ctx.move_to(2, 120) + ctx.show_text("GPS: ") + ctx.move_to(140, 120) + ctx.show_text('On' if self.cfg['gps'] else 'Off') + + # Rechte Seite + ctx.move_to(202, 80) + ctx.show_text("Wifi: ") + ctx.move_to(340, 80) + ctx.show_text('On') + + ctx.move_to(202, 100) + ctx.show_text("Buzzer: ") + ctx.move_to(340, 100) + ctx.show_text('60%') + + ctx.move_to(202, 120) + ctx.show_text("Timezone: ") + ctx.move_to(340, 120) + ctx.show_text(datetime.datetime.now().astimezone().tzname()) + + ctx.stroke() + + # Geräteliste + ctx.move_to(2, 150) + ctx.show_text("NMEA2000 Device List") + + ctx.set_line_width(1.5) + ctx.rectangle(2, 155, 394, 100) + ctx.stroke() + + def draw_devlist(self, ctx): + # NMEA2000 Geräteliste, Vollbild + # scrollen mit Up/Down + pass + + def draw(self, ctx): + if self.standby: + return + self.draw_info(ctx) diff --git a/pages/threevalues.py b/pages/threevalues.py new file mode 100644 index 0000000..0c1093e --- /dev/null +++ b/pages/threevalues.py @@ -0,0 +1,47 @@ +import cairo +from .page import Page + +class ThreeValues(Page): + + def __init__(self, pageno, cfg, boatdata, boatvalue1, boatvalue2, boatvalue3): + super().__init__(pageno, cfg, boatdata) + self.ref1 = self.bd.getRef(boatvalue1) + self.ref2 = self.bd.getRef(boatvalue2) + self.ref3 = self.bd.getRef(boatvalue3) + + def draw(self, ctx): + + # Seitenlayout + ctx.rectangle(0, 105, 400, 3) + ctx.rectangle(0, 195, 400, 3) + ctx.fill() + + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + + # Titel + ctx.set_font_size(40) + ctx.move_to(20, 55) + ctx.show_text(self.ref1.valname) + ctx.move_to(20, 145) + ctx.show_text(self.ref2.valname) + ctx.move_to(20, 235) + ctx.show_text(self.ref3.valname) + + # Einheiten + ctx.set_font_size(24) + ctx.move_to(20, 90) + ctx.show_text(self.ref1.unit) + ctx.move_to(20, 180) + ctx.show_text(self.ref2.unit) + ctx.move_to(20, 270) + ctx.show_text(self.ref3.unit) + + ctx.select_font_face("DSEG7 Classic") + ctx.set_font_size(60) + ctx.move_to(180, 90) + ctx.show_text(self.ref1.format()) + ctx.move_to(180, 180) + ctx.show_text(self.ref2.format()) + ctx.move_to(180, 270) + ctx.show_text(self.ref3.format()) + ctx.stroke() diff --git a/pages/twographs.py b/pages/twographs.py new file mode 100644 index 0000000..0c26ccb --- /dev/null +++ b/pages/twographs.py @@ -0,0 +1,14 @@ +import cairo +from .page import Page + +class TwoGraphs(Page): + + def draw(self, ctx): + # Name + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(60) + ctx.move_to(20, 100) + ctx.show_text("Two Graphs") + + # Zwei Graphen anzeigen X/Y nebeneinander + diff --git a/pages/twovalues.py b/pages/twovalues.py new file mode 100644 index 0000000..5f055e6 --- /dev/null +++ b/pages/twovalues.py @@ -0,0 +1,67 @@ +""" + +Anzeige von zwei frei definierbaren Werten + +Layout ++--------------------+ +| 1 | ++--------------------+ +| 2 | ++--------------------+ + +""" + +import cairo +from .page import Page + +class TwoValues(Page): + + def __init__(self, pageno, cfg, boatdata, boatvalue1, boatvalue2): + super().__init__(pageno, cfg, boatdata) + self.ref1 = self.bd.getRef(boatvalue1) + self.ref2 = self.bd.getRef(boatvalue2) + #print(self.ref1.valname) + + def draw(self, ctx): + + # Seitenunterteilung + ctx.rectangle(0, 145, 400, 3) + ctx.fill() + + # Name + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(40) + ctx.move_to(20, 80) + ctx.show_text(self.ref1.valname) + ctx.move_to(20, 190) + ctx.show_text(self.ref2.valname) + + # Einheit + ctx.set_font_size(24) + ctx.move_to(20, 130) + ctx.show_text(self.ref1.unit) + ctx.move_to(20, 240) + ctx.show_text(self.ref2.unit) + + # Meßwerte + if type(self.ref1 == 'BoatValueGeo'): + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + ctx.set_font_size(40) + ctx.move_to(140, 100) + else: + ctx.select_font_face("DSEG7 Classic") + ctx.set_font_size(84) + ctx.move_to(180, 130) + ctx.show_text(self.ref1.format()) + + if type(self.ref2 == 'BoatValueGeo'): + ctx.select_font_face("Ubuntu") + ctx.set_font_size(40) + ctx.move_to(140, 210) + else: + ctx.select_font_face("DSEG7 Classic") + ctx.set_font_size(84) + ctx.move_to(180, 240) + ctx.show_text(self.ref2.format()) + + ctx.stroke() diff --git a/pages/voltage.py b/pages/voltage.py new file mode 100644 index 0000000..057d06b --- /dev/null +++ b/pages/voltage.py @@ -0,0 +1,216 @@ +""" + +Integrierte Spannungsmessung + +Ideen: + - Umschaltung Datenquelle: intern, N2K + - Umschaltung analog / digital / Graphik + - Historische Werte vom YD-Batteriemonitor + +""" + +import cairo +import math +from .page import Page + +class Voltage(Page): + + avg = (1, 10, 60, 300); + + def __init__(self, pageno, cfg, boatdata): + super().__init__(pageno, cfg, boatdata) + self.trend = True + self.mode = 'A' + self.avgindex = 0 + self.buttonlabel[1] = 'AVG' + self.buttonlabel[2] = 'MODE' + self.buttonlabel[5] = 'TRD' + self.lastvalue = self.bd.voltage.value + + def handle_key(self, buttonid): + if buttonid == 1: + if self.avgindex < len(self.avg) -1: + self.avgindex += 1 + else: + self.avgindex = 0 + elif buttonid == 2: + if self.mode == 'A': + self.mode = 'D' + else: + self.mode = 'A' + elif buttonid == 5: + self.trend = not self.trend + + def setBoatValue(self, boatvalue): + # Einstellen welcher Wert dargestellt werden soll + self.value1 = boatvalue + + def draw(self, ctx): + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + if self.mode == 'A': + self.draw_analog(ctx) + else: + self.draw_digital(ctx) + + def draw_analog(self, ctx): + # TODO schönes Viertelkreis-Instrument + # Skala von 9V bis 15V + # d.h. 90° entsprechend unterteilen in 6 Stücke je 15° + # Beschriftung 10, 11, 12, 13, 14 + + # Datenquelle + ctx.set_font_size(16) + ctx.move_to(300, 40) + ctx.show_text("Source:") + ctx.move_to(300, 60) + ctx.show_text("VBat") + + # Batterietyp + ctx.move_to(300, 90) + ctx.show_text("Type:") + ctx.move_to(300, 110) + ctx.show_text("AGM") + + # Glättung + ctx.move_to(300, 140) + ctx.show_text("Avg:") + ctx.move_to(300, 160) + ctx.show_text("1s") + ctx.stroke() + + # Gleichstromsymbol + ctx.set_font_size(32) + ctx.move_to(20, 80) + ctx.show_text("V") + ctx.set_line_width(3) + ctx.move_to(20, 86.5) # obere Linie + ctx.line_to(40, 86.5) + ctx.move_to(20, 91.5) # untere drei kurzen Linien + ctx.line_to(25, 91.5) + ctx.move_to(27, 91.5) + ctx.line_to(33, 91.5) + ctx.move_to(35, 91.5) + ctx.line_to(40, 91.5) + ctx.stroke() + + # Kreis-segment 90° + + # Rotationszentrum + cx = 260 + cy = 270 + + # Radius des Instruments + r = 240 + + ctx.set_source_rgb(*self.fgcolor) + ctx.set_line_width(2) + + ctx.arc(cx, cy, r, math.pi, 1.5*math.pi) + ctx.stroke() + + + # Beschriftung + ctx.set_font_size(20) + label = {285: "10", 300: "11", 315: "12", 330: "13", 345: "14"} + for angle in label: + x, y = self.rotate((cx, cy), ((cx, cy - r + 30),), angle)[0] + self.draw_text_center(ctx, x, y, label[angle]) + + # grobe Skala + p = ((cx, cy-r), (cx, cy - r + 12)) + ctx.set_line_width(2) + for angle in label: + line = self.rotate((cx, cy), p, angle) + ctx.move_to(*line[0]) + ctx.line_to(*line[1]) + ctx.stroke() + + # feine Skala + p = ((cx, cy-r), (cx, cy - r + 5)) + ctx.set_line_width(1) + for angle in [x for x in range(273, 360, 3)]: + if angle in label: + continue + line = self.rotate((cx, cy), p, angle) + ctx.move_to(*line[0]) + ctx.line_to(*line[1]) + ctx.stroke() + + # Zeiger auf 0-Position + val = float(self.bd.voltage.format()) + if not val: + angle = -0.5 + elif val > 15: + angle = 91 + elif val <= 9: + angle = -0.5 + else: + angle = (val - 9) * 15 + p = ((cx - 2, cy + 4), + (cx - r + 35, cy + 2), + (cx - r + 35, cy + 1), + (cx - r + 5, cy + 1), + (cx - r + 5, cy - 1), + (cx - r + 35, cy - 1), + (cx - r + 35, cy - 2), + (cx - 2, cy - 4)) + zeiger = self.rotate((cx, cy), p, angle) + + # Zeiger zeichnen + ctx.set_line_width(1) + ctx.move_to(*zeiger[0]) + for point in zeiger[1:]: + ctx.line_to(*point) + ctx.fill() + + # Zeigerbasis + ctx.arc(cx, cy, 6, 0, 2*math.pi) + ctx.set_source_rgb(*self.bgcolor) + ctx.fill_preserve() + ctx.set_line_width(2) + ctx.set_source_rgb(*self.fgcolor) + ctx.stroke() + + def draw_digital(self, ctx): + # Name + ctx.set_font_size(60) + ctx.move_to(20, 100) + ctx.show_text("VBat") + # Unit + ctx.set_font_size(40) + ctx.move_to(270, 100) + ctx.show_text("V") + # Battery type + ctx.set_font_size(16) + ctx.move_to(294, 100) + ctx.show_text("AGM") + # Mittelwert + ctx.move_to(320, 84) + ctx.show_text("Avg: {}s".format(self.avg[self.avgindex])) + ctx.stroke() + + ctx.select_font_face("DSEG7 Classic") + ctx.set_font_size(100) + ctx.move_to(40, 240) + ctx.show_text(self.bd.voltage.format()) + ctx.stroke() + + # Trendanzeige + if self.trend and self.bd.voltage.value and self.lastvalue: + ctx.rectangle(315, 183, 35, 4) + ctx.fill() + size = 11 + if self.lastvalue < self.bd.voltage.value: + ctx.move_to(320, 174) + ctx.line_to(320+size*2, 174) + ctx.line_to(320+size, 174-size*2) + ctx.line_to(320, 174) + ctx.fill() + elif self.lastvalue > self.bd.voltage.value: + ctx.move_to(320, 195) + ctx.line_to(320+size*2, 195) + ctx.line_to(320+size, 195+size*2) + ctx.line_to(320, 195) + ctx.fill() + + self.lastvalue = self.bd.voltage.value diff --git a/pages/windrose.py b/pages/windrose.py new file mode 100644 index 0000000..4951a3a --- /dev/null +++ b/pages/windrose.py @@ -0,0 +1,119 @@ +""" + +Windrose und Windroseflex +Benötigt 6 Werte +Hauptwerte: AWA, AWS, TWD, TWS +Nebenwerte: DBT, STW, oder COG, SOG + +""" + +import cairo +import math +from .page import Page + +class WindRose(Page): + + def draw(self, ctx): + + # Rahmen + cx = 200 + cy = 150 + r = 110 + + ctx.set_line_width(3) + ctx.arc(cx, cy, r + 9, 0, 2*math.pi) + ctx.stroke() + ctx.arc(cx, cy, r - 11, 0, 2*math.pi) + ctx.stroke() + + for angle in range(0, 360, 10): + if angle % 30 != 0: + x, y = self.rotate((cx, cy), ((cx, cy - r),), angle)[0] + ctx.arc(x, y, 2, 0, 2*math.pi) + ctx.fill() + else: + p = ((cx, cy - r + 10), (cx, cy - r - 10), (cx, cy - r + 30)) + pr = self.rotate((cx, cy), p, angle) + ctx.move_to(*pr[0]) + ctx.line_to(*pr[1]) + ctx.stroke() + self.draw_text_center(ctx, *pr[2], str(angle)) + + + ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) + + # Namen + ctx.set_font_size(24) + ctx.move_to(10, 95) # links oben + ctx.show_text("AWA") + ctx.move_to(335, 95) # rechts oben + ctx.show_text("TWD") + ctx.move_to(10, 220) # links unten + ctx.show_text("AWS") + ctx.move_to(335, 220) # rechts unten + ctx.show_text("TWS") + + # Units + ctx.set_font_size(16) + ctx.move_to(10, 115) # links oben + ctx.show_text("Deg") + ctx.move_to(335, 115) # rechts oben + ctx.show_text("Deg") + ctx.move_to(10, 190) # links unten + ctx.show_text("kn") + ctx.move_to(335, 190) # rechts unten + ctx.show_text("kn") + + # Horiz. Trennlinien + #ctx.rectangle(0, 149, 60, 3) + #ctx.fill() + ctx.set_line_width(3) + # links + ctx.move_to(0, 149) + ctx.line_to(60, 149) + # rechts + ctx.move_to(340, 149) + ctx.line_to(400, 149) + ctx.stroke() + + # Meßwerte + ctx.select_font_face("DSEG7 Classic") + ctx.set_font_size(40) + # links oben + ctx.move_to(10, 65) + ctx.show_text("148") + # rechts oben + ctx.move_to(295, 65) + ctx.show_text("---") + # links unten + ctx.move_to(10, 270) + ctx.show_text("46.7") + # rechts unten + ctx.move_to(295, 270) + ctx.show_text("77.8") + + ctx.set_font_size(32) + # innen oben + ctx.move_to(160, 130) + ctx.show_text("38.9") + # innen unten + ctx.move_to(160, 200) + ctx.show_text("19.9") + ctx.stroke() + + + # Zeiger + angle = 148 + p = ((cx - 1, cy - (r - 15)), (cx + 1, cy - (r - 15)), (cx + 4, cy), (cx - 4, cy)) + zeiger = self.rotate((cx, cy), p, angle) + ctx.move_to(*zeiger[0]) + for point in zeiger[1:]: + ctx.line_to(*point) + ctx.fill() + + ctx.set_source_rgb(*self.bgcolor) + ctx.arc(cx, cy, 8, 0, 2*math.pi) + ctx.fill() + ctx.set_source_rgb(*self.fgcolor) + ctx.arc(cx, cy, 7, 0, 2*math.pi) + ctx.fill()