Compare commits
No commits in common. "47f43132c6b950acf282f5e633cee6b2da843d6c" and "bc548b767abd794439d030f0ab06669d0110904c" have entirely different histories.
47f43132c6
...
bc548b767a
Binary file not shown.
100
REAME.md
100
REAME.md
|
|
@ -1,30 +1,33 @@
|
||||||
# RASPBERRY PI ALS ZENTRALRECHNER FUER BIENENSTOCKUEBERWACHUNG
|
# RASPBERRY PI ALS ZENTRALRECHNER FUER BIENENSTOCKUEBERWACHUNG
|
||||||
|
|
||||||
## Hardware
|
## Hardware Variante A
|
||||||
- Raspberry Pi Zero W
|
- Raspberry Pi Zero W
|
||||||
- 3G/4G Stick Huawei E3533
|
- 3G Stick Huawei E3531
|
||||||
|
|
||||||
|
## Hardware Variante B (Kurt Jakob)
|
||||||
|
- Raspberry Pi 3 Model B
|
||||||
|
- Itead Raspberry Pi GSM/GPRS Board (SIM800)
|
||||||
|
- ModMyPi Itead GSM Board Gehäuse
|
||||||
|
|
||||||
## INSTALLATION
|
## INSTALLATION
|
||||||
|
|
||||||
### Grundinstallation
|
### Grundinstallation
|
||||||
```
|
```
|
||||||
Image: 2018-04-18-raspbian-stretch-lite.zip von https://www.raspberrypi.org/downloads/raspbian/
|
Image: 2017-09-07-raspbian-stretch-lite.zip von https://www.raspberrypi.org/downloads/raspbian/
|
||||||
|
|
||||||
Installation auf SD:
|
Installation auf SD:
|
||||||
# unzip -p 2018-04-18-raspbian-stretch-lite.zip |dd of=/dev/sdXXX bs=4M conv=fsync
|
# unzip -p 2017-09-07-raspbian-stretch-lite.zip |dd of=/dev/sdXXX bs=4M conv=fsync
|
||||||
|
|
||||||
# raspi-config
|
# raspi-config
|
||||||
- Change User Password: meielis-b...
|
- Change User Password: meielis-...
|
||||||
- Hostname: beielipi
|
- Hostname: beielipi
|
||||||
- enable predicatble network interface names
|
|
||||||
- locale: en_US.utf8 (auch Default)
|
- locale: en_US.utf8 (auch Default)
|
||||||
- locale: de_CH.utf8
|
- locale: de_CH.utf8
|
||||||
- Timezone: Europe/Zurich
|
- Timezone: Europe/Zurich
|
||||||
- Change Wifi Country: CH
|
- Change Wifi Country: CH
|
||||||
- Keyboard: Generic 105-key (Intl), German (Switzerland)
|
- Keyboard: Generic 105-key (Intl), German (Switzerland)
|
||||||
- Enable SSH Server
|
- Enable SSH Server
|
||||||
- WLAN einrichten (siehe "per WLAN")
|
- Fuer Variante A: WLAN einrichten (siehe "per WLAN")
|
||||||
- reboot
|
|
||||||
|
|
||||||
# apt-get update
|
# apt-get update
|
||||||
# apt-get upgrade
|
# apt-get upgrade
|
||||||
|
|
@ -40,10 +43,9 @@ Software installieren:
|
||||||
# apt-get install ntpdate
|
# apt-get install ntpdate
|
||||||
# apt-get install dnsmasq
|
# apt-get install dnsmasq
|
||||||
# apt-get install python-bottle
|
# apt-get install python-bottle
|
||||||
# apt-get install python-pip
|
|
||||||
# apt-get install libglib2.0-dev
|
|
||||||
|
|
||||||
# pip install bluepy
|
Firmware-Update:
|
||||||
|
# rpi-update
|
||||||
```
|
```
|
||||||
|
|
||||||
### Zugang ermöglichen
|
### Zugang ermöglichen
|
||||||
|
|
@ -59,41 +61,18 @@ network={
|
||||||
ssid="haerdoepfu27" psk="XXXXXXXXXXXX"
|
ssid="haerdoepfu27" psk="XXXXXXXXXXXX"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
#### per LAN
|
||||||
### Boot Config anpassen
|
|
||||||
|
|
||||||
in /boot/config.txt
|
|
||||||
|
|
||||||
```
|
```
|
||||||
dtparam=audio=off
|
root@beielipi:~# cat /etc/network/interfaces.d/eth0
|
||||||
|
auto eth0
|
||||||
|
iface eth0 inet static
|
||||||
|
address 192.168.0.50
|
||||||
|
netmask 255.255.255.0
|
||||||
```
|
```
|
||||||
|
|
||||||
### Strom sparen
|
### Variante A
|
||||||
|
|
||||||
```
|
|
||||||
/etc/rc.local einfuegen:
|
|
||||||
# Disable HDMI to save power
|
|
||||||
# Joerg Lehmann, 2.4.2018
|
|
||||||
/usr/bin/tvservice -o
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
```
|
|
||||||
|
|
||||||
=== Blacklist Modules
|
|
||||||
|
|
||||||
```
|
|
||||||
/etc/modprobe.d/raspi-blacklist.conf:
|
|
||||||
blacklist spi-bcm2708
|
|
||||||
blacklist i2c-bcm2708
|
|
||||||
blacklist huawei_cdc_ncm
|
|
||||||
blacklist sr_mod
|
|
||||||
blacklist cdrom
|
|
||||||
blacklist usb-storage
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### Einrichten GSM
|
|
||||||
```
|
```
|
||||||
|
Einrichten GSM:
|
||||||
/etc/usb_modeswitch.d/12d1:157d:
|
/etc/usb_modeswitch.d/12d1:157d:
|
||||||
#Huawei E3533
|
#Huawei E3533
|
||||||
TargetVendor=0x12d1
|
TargetVendor=0x12d1
|
||||||
|
|
@ -102,14 +81,10 @@ MessageContent="55534243123456780000000000000011063000000000010000000000000000"
|
||||||
NoMBIMCheck=1
|
NoMBIMCheck=1
|
||||||
|
|
||||||
/etc/usb_modeswitch.conf:
|
/etc/usb_modeswitch.conf:
|
||||||
DisableMBIMGlobal=1
|
|
||||||
SetStorageDelay=8
|
SetStorageDelay=8
|
||||||
|
|
||||||
Workaround fuer USB disconnects:
|
Workaround fuer USB disconnects:
|
||||||
|
|
||||||
In /boot/cmdline.txt folgendes anhaengen:
|
|
||||||
dwc_otg.speed=1
|
|
||||||
|
|
||||||
/etc/cron.d/reset_usb:
|
/etc/cron.d/reset_usb:
|
||||||
# Reset USB if device names disappear
|
# Reset USB if device names disappear
|
||||||
* * * * * root [ ! -c /dev/ttyUSB0 ] && /usr/local/bin/usb_off_on.sh 2>/dev/null
|
* * * * * root [ ! -c /dev/ttyUSB0 ] && /usr/local/bin/usb_off_on.sh 2>/dev/null
|
||||||
|
|
@ -141,6 +116,22 @@ OK AT+CPMS="SM";+CNMI=2,0,0,2,1
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Variante B
|
||||||
|
```
|
||||||
|
# systemctl disable hciuart.service
|
||||||
|
|
||||||
|
# cat /boot/config.txt
|
||||||
|
...
|
||||||
|
# Wegen GSM Modul
|
||||||
|
dtoverlay=pi3-miniuart-bt
|
||||||
|
#dtoverlay=pi3-disable-bt
|
||||||
|
enable_uart=1
|
||||||
|
===
|
||||||
|
|
||||||
|
# cat /boot/cmdline.txt
|
||||||
|
dwc_otg.lpm_enable=0 root=PARTUUID=61612258-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait
|
||||||
|
===
|
||||||
|
```
|
||||||
|
|
||||||
### Test SMS-Versand:
|
### Test SMS-Versand:
|
||||||
```
|
```
|
||||||
|
|
@ -176,22 +167,15 @@ beieli@beielipi:~ $ exit
|
||||||
# for i in $(find . -type f); do echo cp $i /$i ; done
|
# for i in $(find . -type f); do echo cp $i /$i ; done
|
||||||
# for i in $(find . -type f); do cp $i /$i ; done
|
# for i in $(find . -type f); do cp $i /$i ; done
|
||||||
|
|
||||||
|
Fuer Variante A wieder loeschen:
|
||||||
|
# rm /etc/cron.d/gsm_poweron
|
||||||
|
|
||||||
# systemctl enable beielimon
|
# systemctl enable beielimon
|
||||||
# systemctl enable btmon
|
|
||||||
# systemctl enable smsmon
|
# systemctl enable smsmon
|
||||||
# systemctl disable hostapd
|
Fuer Variante B:
|
||||||
# systemctl disable fakedns
|
# systemctl disable dhcpcd
|
||||||
# systemctl disable dnsmasq
|
|
||||||
# systemctl disable avahi-daemon
|
|
||||||
# systemctl disable triggerhappy
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/etc/fstab anpassen:
|
|
||||||
tmpfs /home/beieli/bt-readings tmpfs nodev,nosuid,uid=1001,gid=2000,size=4M 0 0
|
|
||||||
|
|
||||||
# mkdir /home/beieli/bt-readings
|
|
||||||
|
|
||||||
Reboot:
|
Reboot:
|
||||||
# init 6
|
# init 6
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,5 +20,6 @@ mailfrom: info@nbit.ch
|
||||||
mailto: joerg.lehmann@nbit.ch
|
mailto: joerg.lehmann@nbit.ch
|
||||||
mailuser: nbitinf@nbit.ch
|
mailuser: nbitinf@nbit.ch
|
||||||
mailpwd: ukihefak27
|
mailpwd: ukihefak27
|
||||||
balance_ussd: "*130#"
|
balance_ussd: "*121#"
|
||||||
|
master_sms_number: "+41765006123"
|
||||||
manipulation_duration_minutes: 60
|
manipulation_duration_minutes: 60
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ class Scale(object):
|
||||||
prefix = 'accu'
|
prefix = 'accu'
|
||||||
datafilename = "%s/data/%s-%s-%s%s%s.log" % (APP_ROOT,prefix,self.scale_config['scale_uuid'],year,month,day)
|
datafilename = "%s/data/%s-%s-%s%s%s.log" % (APP_ROOT,prefix,self.scale_config['scale_uuid'],year,month,day)
|
||||||
with open(datafilename, 'a') as file:
|
with open(datafilename, 'a') as file:
|
||||||
file.write('%s,%.2f\n' % (timestamp,self.accu / 100.0))
|
file.write('%s,%.1f\n' % (timestamp,self.accu / 100.0))
|
||||||
|
|
||||||
if self.hum != INVALID_VALUE and not(swarm_alarm):
|
if self.hum != INVALID_VALUE and not(swarm_alarm):
|
||||||
prefix = 'humidity'
|
prefix = 'humidity'
|
||||||
|
|
@ -97,7 +97,7 @@ class Scale(object):
|
||||||
prefix = 'temp'
|
prefix = 'temp'
|
||||||
datafilename = "%s/data/%s-%s-%s%s%s.log" % (APP_ROOT,prefix,self.scale_config['scale_uuid'],year,month,day)
|
datafilename = "%s/data/%s-%s-%s%s%s.log" % (APP_ROOT,prefix,self.scale_config['scale_uuid'],year,month,day)
|
||||||
with open(datafilename, 'a') as file:
|
with open(datafilename, 'a') as file:
|
||||||
file.write('%s,%.1f\n' % (timestamp,self.temp / 10.0))
|
file.write('%s,%d\n' % (timestamp,self.temp / 10))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ ssid=beielipi
|
||||||
# First up, the SSID or Network name. This is what other devices will see when they try to connect.
|
# First up, the SSID or Network name. This is what other devices will see when they try to connect.
|
||||||
hw_mode=g
|
hw_mode=g
|
||||||
# I'm setting this to Wireless G mode. A, B, and G are available here.
|
# I'm setting this to Wireless G mode. A, B, and G are available here.
|
||||||
channel=7
|
channel=1
|
||||||
# This is setting the channel that the WiFi is on, valid channels are from 1-11, or 1-14 depending on location.
|
# This is setting the channel that the WiFi is on, valid channels are from 1-11, or 1-14 depending on location.
|
||||||
|
|
||||||
# Wifi Security Settings
|
# Wifi Security Settings
|
||||||
|
|
@ -20,10 +20,6 @@ channel=7
|
||||||
# The line above sets the wpa passphrase to "raspiwlan", this is obtained via the wpa_passphrase command.
|
# The line above sets the wpa passphrase to "raspiwlan", this is obtained via the wpa_passphrase command.
|
||||||
# However, you can also set a passphrase like the line below.
|
# However, you can also set a passphrase like the line below.
|
||||||
#wpa_passphrase=raspiwlan
|
#wpa_passphrase=raspiwlan
|
||||||
wpa=2
|
|
||||||
wpa_key_mgmt=WPA-PSK
|
|
||||||
wpa_pairwise=CCMP
|
|
||||||
wpa_passphrase=beielipi
|
|
||||||
|
|
||||||
#wpa_key_mgmt=WPA-PSK
|
#wpa_key_mgmt=WPA-PSK
|
||||||
#wpa_pairwise=CCMP
|
#wpa_pairwise=CCMP
|
||||||
|
|
@ -34,10 +30,5 @@ wpa_passphrase=beielipi
|
||||||
|
|
||||||
# Other settings
|
# Other settings
|
||||||
beacon_int=100 # This sets how often the WiFi will send a beacon out.
|
beacon_int=100 # This sets how often the WiFi will send a beacon out.
|
||||||
auth_algs=1
|
auth_algs=3
|
||||||
wmm_enabled=0
|
wmm_enabled=1
|
||||||
|
|
||||||
# see https://raspberrypi.stackexchange.com/questions/11713/rtl8188cus-extremely-slow
|
|
||||||
#wme_enabled=1
|
|
||||||
#ieee80211n=1
|
|
||||||
#ht_capab=[HT40+][SHORT-GI-40][DSSS_CCK-40]
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#imis/internet is the apn for idea connection
|
#imis/internet is the apn for idea connection
|
||||||
connect "/usr/sbin/chat -v -f /etc/chatscripts/gprs -T gprs.swisscom.ch"
|
#connect "/usr/sbin/chat -v -f /etc/chatscripts/gprs -T gprs.swisscom.ch"
|
||||||
#connect "/usr/sbin/chat -v -f /etc/chatscripts/gprs -T internet"
|
connect "/usr/sbin/chat -v -f /etc/chatscripts/gprs -T internet"
|
||||||
|
|
||||||
# For Raspberry Pi3 use /dev/ttyS0 as the communication port:
|
# For Raspberry Pi3 use /dev/ttyS0 as the communication port:
|
||||||
/dev/ttyUSB0
|
/dev/ttyUSB0
|
||||||
|
|
@ -26,8 +26,8 @@ persist
|
||||||
# Do not ask the remote to authenticate.
|
# Do not ask the remote to authenticate.
|
||||||
noauth
|
noauth
|
||||||
|
|
||||||
# hardware flow control on the serial link with GSM Modem
|
# No hardware flow control on the serial link with GSM Modem
|
||||||
crtscts
|
nocrtscts
|
||||||
|
|
||||||
# No modem control lines with GSM Modem
|
# No modem control lines with GSM Modem
|
||||||
local
|
local
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ Type=simple
|
||||||
User=beieli
|
User=beieli
|
||||||
Group=beieli
|
Group=beieli
|
||||||
WorkingDirectory=/home/beieli/bin
|
WorkingDirectory=/home/beieli/bin
|
||||||
ExecStartPre=/bin/sleep 10
|
|
||||||
ExecStart=/home/beieli/bin/smsmon.py
|
ExecStart=/home/beieli/bin/smsmon.py
|
||||||
Restart=always
|
Restart=always
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
#
|
||||||
|
# Simuliert das Druecken des Powerkeys, um das GSM-Modul an- oder abzuschalten
|
||||||
|
#
|
||||||
|
import time
|
||||||
|
import RPi.GPIO as GPIO
|
||||||
|
|
||||||
|
# RPi.GPIO Layout verwenden (wie Pin-Nummern)
|
||||||
|
GPIO.setmode(GPIO.BOARD)
|
||||||
|
|
||||||
|
# Pin 11 (GPIO 17) auf Output setzen
|
||||||
|
GPIO.setup(11, GPIO.OUT)
|
||||||
|
|
||||||
|
# Druecken simulieren
|
||||||
|
GPIO.output(11, GPIO.HIGH)
|
||||||
|
|
||||||
|
# Pause, mindestens eine Sekunde gemaess https://www.itead.cc/wiki/RPI_SIM800_GSM/GPRS_ADD-ON_V2.0
|
||||||
|
time.sleep(1.5)
|
||||||
|
|
||||||
|
# Weg vom Taster...
|
||||||
|
GPIO.output(11, GPIO.LOW)
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
GPIO.cleanup()
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
#
|
||||||
|
# GSM-Modul anschalten, falls ein keine Antwort gibt...
|
||||||
|
#
|
||||||
|
import time
|
||||||
|
import RPi.GPIO as GPIO
|
||||||
|
import serial
|
||||||
|
|
||||||
|
def PowerOn():
|
||||||
|
# RPi.GPIO Layout verwenden (wie Pin-Nummern)
|
||||||
|
GPIO.setmode(GPIO.BOARD)
|
||||||
|
|
||||||
|
# Pin 11 (GPIO 17) auf Output setzen
|
||||||
|
GPIO.setup(11, GPIO.OUT)
|
||||||
|
|
||||||
|
# Druecken simulieren
|
||||||
|
GPIO.output(11, GPIO.HIGH)
|
||||||
|
|
||||||
|
# Pause, mindestens eine Sekunde gemaess https://www.itead.cc/wiki/RPI_SIM800_GSM/GPRS_ADD-ON_V2.0
|
||||||
|
time.sleep(1.5)
|
||||||
|
|
||||||
|
# Weg vom Taster...
|
||||||
|
GPIO.output(11, GPIO.LOW)
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
GPIO.cleanup()
|
||||||
|
|
||||||
|
with serial.Serial('/dev/ttyAMA0', 115200, timeout=2) as ser:
|
||||||
|
ser.write(b'AT\n')
|
||||||
|
line = ser.readline()
|
||||||
|
line = ser.readline()
|
||||||
|
print "Feedback: %s" % (line)
|
||||||
|
if line.strip() != "OK":
|
||||||
|
print "Modem reagiert nicht, evtl. abgeschaltet... wir schalten es ein..."
|
||||||
|
PowerOn()
|
||||||
|
ser.close() # close port
|
||||||
|
|
@ -128,7 +128,6 @@ def GetInfoText():
|
||||||
['uname', '-a'],
|
['uname', '-a'],
|
||||||
['df','-h','/boot','/root'],
|
['df','-h','/boot','/root'],
|
||||||
['free','-m'],
|
['free','-m'],
|
||||||
['/home/beieli/root-bin/show-bt-readings'],
|
|
||||||
['ip','a'] ]:
|
['ip','a'] ]:
|
||||||
commands_output = "%s\n# %s\n%s" % (commands_output,' '.join(comm_arr),subprocess.check_output(comm_arr))
|
commands_output = "%s\n# %s\n%s" % (commands_output,' '.join(comm_arr),subprocess.check_output(comm_arr))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
find /home/beieli/bt-readings -type f -print -exec cat {} \;
|
|
||||||
echo
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<h3>BeieliPi Zugang via WLAN-Hotspot</h3>
|
<h3>BeieliPi Zugang via WLAN-Hotspot</h3>
|
||||||
<p>Zugang zu dieser Seite: Verbinden (WLAN) mit SSID <strong>beielipi</strong> (Passwort: beielipi), dann Aufruf von <strong>http://beielipi.local</strong></p>
|
<p>Zugang zu dieser Seite: Verbinden (WLAN) mit SSID <strong>beielipi</strong> (kein Passwort), dann Aufruf von <strong>http://beielipi.local</strong></p>
|
||||||
<p>Hotspot aktivieren per SMS an {{ beielipi_mobile_number }}, Text: "hotspot on" (zum Deaktivieren: "hotspot off")</p>
|
<p>Hotspot aktivieren per SMS an {{ beielipi_mobile_number }}, Text: "hotspot on" (zum Deaktivieren: "hotspot off")</p>
|
||||||
</div>
|
</div>
|
||||||
<div id="navbar" class="navbar-collapse collapse">
|
<div id="navbar" class="navbar-collapse collapse">
|
||||||
|
|
|
||||||
|
|
@ -1,140 +0,0 @@
|
||||||
(function() {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var Dygraph;
|
|
||||||
if (window.Dygraph) {
|
|
||||||
Dygraph = window.Dygraph;
|
|
||||||
} else if (typeof(module) !== 'undefined') {
|
|
||||||
Dygraph = require('../dygraph');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given three sequential points, p0, p1 and p2, find the left and right
|
|
||||||
* control points for p1.
|
|
||||||
*
|
|
||||||
* The three points are expected to have x and y properties.
|
|
||||||
*
|
|
||||||
* The alpha parameter controls the amount of smoothing.
|
|
||||||
* If α=0, then both control points will be the same as p1 (i.e. no smoothing).
|
|
||||||
*
|
|
||||||
* Returns [l1x, l1y, r1x, r1y]
|
|
||||||
*
|
|
||||||
* It's guaranteed that the line from (l1x, l1y)-(r1x, r1y) passes through p1.
|
|
||||||
* Unless allowFalseExtrema is set, then it's also guaranteed that:
|
|
||||||
* l1y ∈ [p0.y, p1.y]
|
|
||||||
* r1y ∈ [p1.y, p2.y]
|
|
||||||
*
|
|
||||||
* The basic algorithm is:
|
|
||||||
* 1. Put the control points l1 and r1 α of the way down (p0, p1) and (p1, p2).
|
|
||||||
* 2. Shift l1 and r2 so that the line l1–r1 passes through p1
|
|
||||||
* 3. Adjust to prevent false extrema while keeping p1 on the l1–r1 line.
|
|
||||||
*
|
|
||||||
* This is loosely based on the HighCharts algorithm.
|
|
||||||
*/
|
|
||||||
function getControlPoints(p0, p1, p2, opt_alpha, opt_allowFalseExtrema) {
|
|
||||||
var alpha = (opt_alpha !== undefined) ? opt_alpha : 1/3; // 0=no smoothing, 1=crazy smoothing
|
|
||||||
var allowFalseExtrema = opt_allowFalseExtrema || false;
|
|
||||||
|
|
||||||
if (!p2) {
|
|
||||||
return [p1.x, p1.y, null, null];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 1: Position the control points along each line segment.
|
|
||||||
var l1x = (1 - alpha) * p1.x + alpha * p0.x,
|
|
||||||
l1y = (1 - alpha) * p1.y + alpha * p0.y,
|
|
||||||
r1x = (1 - alpha) * p1.x + alpha * p2.x,
|
|
||||||
r1y = (1 - alpha) * p1.y + alpha * p2.y;
|
|
||||||
|
|
||||||
// Step 2: shift the points up so that p1 is on the l1–r1 line.
|
|
||||||
if (l1x != r1x) {
|
|
||||||
// This can be derived w/ some basic algebra.
|
|
||||||
var deltaY = p1.y - r1y - (p1.x - r1x) * (l1y - r1y) / (l1x - r1x);
|
|
||||||
l1y += deltaY;
|
|
||||||
r1y += deltaY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 3: correct to avoid false extrema.
|
|
||||||
if (!allowFalseExtrema) {
|
|
||||||
if (l1y > p0.y && l1y > p1.y) {
|
|
||||||
l1y = Math.max(p0.y, p1.y);
|
|
||||||
r1y = 2 * p1.y - l1y;
|
|
||||||
} else if (l1y < p0.y && l1y < p1.y) {
|
|
||||||
l1y = Math.min(p0.y, p1.y);
|
|
||||||
r1y = 2 * p1.y - l1y;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r1y > p1.y && r1y > p2.y) {
|
|
||||||
r1y = Math.max(p1.y, p2.y);
|
|
||||||
l1y = 2 * p1.y - r1y;
|
|
||||||
} else if (r1y < p1.y && r1y < p2.y) {
|
|
||||||
r1y = Math.min(p1.y, p2.y);
|
|
||||||
l1y = 2 * p1.y - r1y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [l1x, l1y, r1x, r1y];
|
|
||||||
}
|
|
||||||
|
|
||||||
// i.e. is none of (null, undefined, NaN)
|
|
||||||
function isOK(x) {
|
|
||||||
return !!x && !isNaN(x);
|
|
||||||
};
|
|
||||||
|
|
||||||
// A plotter which uses splines to create a smooth curve.
|
|
||||||
// See tests/plotters.html for a demo.
|
|
||||||
// Can be controlled via smoothPlotter.smoothing
|
|
||||||
function smoothPlotter(e) {
|
|
||||||
var ctx = e.drawingContext,
|
|
||||||
points = e.points;
|
|
||||||
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(points[0].canvasx, points[0].canvasy);
|
|
||||||
|
|
||||||
// right control point for previous point
|
|
||||||
var lastRightX = points[0].canvasx, lastRightY = points[0].canvasy;
|
|
||||||
|
|
||||||
for (var i = 1; i < points.length; i++) {
|
|
||||||
var p0 = points[i - 1],
|
|
||||||
p1 = points[i],
|
|
||||||
p2 = points[i + 1];
|
|
||||||
p0 = p0 && isOK(p0.canvasy) ? p0 : null;
|
|
||||||
p1 = p1 && isOK(p1.canvasy) ? p1 : null;
|
|
||||||
p2 = p2 && isOK(p2.canvasy) ? p2 : null;
|
|
||||||
if (p0 && p1) {
|
|
||||||
var controls = getControlPoints({x: p0.canvasx, y: p0.canvasy},
|
|
||||||
{x: p1.canvasx, y: p1.canvasy},
|
|
||||||
p2 && {x: p2.canvasx, y: p2.canvasy},
|
|
||||||
smoothPlotter.smoothing);
|
|
||||||
// Uncomment to show the control points:
|
|
||||||
// ctx.lineTo(lastRightX, lastRightY);
|
|
||||||
// ctx.lineTo(controls[0], controls[1]);
|
|
||||||
// ctx.lineTo(p1.canvasx, p1.canvasy);
|
|
||||||
lastRightX = (lastRightX !== null) ? lastRightX : p0.canvasx;
|
|
||||||
lastRightY = (lastRightY !== null) ? lastRightY : p0.canvasy;
|
|
||||||
ctx.bezierCurveTo(lastRightX, lastRightY,
|
|
||||||
controls[0], controls[1],
|
|
||||||
p1.canvasx, p1.canvasy);
|
|
||||||
lastRightX = controls[2];
|
|
||||||
lastRightY = controls[3];
|
|
||||||
} else if (p1) {
|
|
||||||
// We're starting again after a missing point.
|
|
||||||
ctx.moveTo(p1.canvasx, p1.canvasy);
|
|
||||||
lastRightX = p1.canvasx;
|
|
||||||
lastRightY = p1.canvasy;
|
|
||||||
} else {
|
|
||||||
lastRightX = lastRightY = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
||||||
smoothPlotter.smoothing = 1/3;
|
|
||||||
smoothPlotter._getControlPoints = getControlPoints; // for testing
|
|
||||||
|
|
||||||
// older versions exported a global.
|
|
||||||
// This will be removed in the future.
|
|
||||||
// The preferred way to access smoothPlotter is via Dygraph.smoothPlotter.
|
|
||||||
window.smoothPlotter = smoothPlotter;
|
|
||||||
Dygraph.smoothPlotter = smoothPlotter;
|
|
||||||
|
|
||||||
})();
|
|
||||||
Loading…
Reference in New Issue