diff --git a/.gitmodules b/.gitmodules index 0833484b..5e574385 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "dump1090"] path = dump1090 - url = https://github.com/AvSquirrel/dump1090 + url = https://github.com/stratux/dump1090 [submodule "goflying"] path = goflying url = https://github.com/cyoung/goflying diff --git a/Makefile b/Makefile index b4b22d17..14a297f6 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ all: make ahrs_approx xdump978 xdump1090 xgen_gdl90 $(PLATFORMDEPENDENT) xgen_gdl90: - go get -t -d -v ./main ./test ./godump978 ./uatparse ./sensors + go get -t -d -v ./main ./godump978 ./uatparse ./sensors go build $(BUILDINFO) -p 4 main/gen_gdl90.go main/traffic.go main/gps.go main/network.go main/managementinterface.go main/sdr.go main/ping.go main/uibroadcast.go main/monotonic.go main/datalog.go main/equations.go main/sensors.go main/cputemp.go fancontrol: diff --git a/README.md b/README.md index ed411b9b..a020192b 100644 --- a/README.md +++ b/README.md @@ -47,3 +47,5 @@ Jet tests (high gain antennas): * Embraer ERJ 145 * Cessna Citation 501 * Lear 35 +* Rockwell B-1b +* Boeing C-17 diff --git a/circle.yml b/circle.yml index 925e7573..75d1fcde 100644 --- a/circle.yml +++ b/circle.yml @@ -5,13 +5,13 @@ machine: dependencies: pre: - - sudo apt-get update; sudo apt-get install libusb-1.0-0-dev; cd ~/; git clone https://github.com/jpoirier/librtlsdr; cd librtlsdr; mkdir build; cd build; cmake ../; make; sudo make install; sudo ldconfig; cd ~/; mkdir gopath; cd ~/; mkdir gopath; wget https://storage.googleapis.com/golang/go1.6.src.tar.gz; tar -zxvf go1.6.src.tar.gz; cd go/src; export GOROOT_BOOTSTRAP=/usr/local/go; ./make.bash; echo $PATH; echo $GOPATH; go version; env + - sudo apt-get update; sudo apt-get install libusb-1.0-0-dev mercurial; cd ~/; git clone https://github.com/jpoirier/librtlsdr; cd librtlsdr; mkdir build; cd build; cmake ../; make; sudo make install; sudo ldconfig; cd ~/; rm -rf gopath; mkdir gopath; wget https://dl.google.com/go/go1.9.2.linux-amd64.tar.gz; tar -zxvf go1.9.2.linux-amd64.tar.gz; go version; env override: - cd .. ; rm -rf stratux ; git clone --recursive https://github.com/cyoung/stratux ; cd stratux ; git config --add remote.origin.fetch "+refs/pull/*/head:refs/remotes/origin/pr/*" ; git fetch origin ; BRANCH=`echo "$CIRCLE_BRANCH" | sed 's/pull\//pr\//g'` ; git checkout $BRANCH ; make test: override: - - make test + - make deployment: production: diff --git a/image/interfaces b/image/interfaces index a385c88f..1e318e50 100644 --- a/image/interfaces +++ b/image/interfaces @@ -11,6 +11,7 @@ iface wlan0 inet static address 10.26.36.1 netmask 255.255.255.0 post-up /usr/sbin/stratux-wifi.sh + wireless-power off auto eth0:0 iface eth0:0 inet static diff --git a/image/logrotate.conf b/image/logrotate.conf new file mode 100644 index 00000000..073056d0 --- /dev/null +++ b/image/logrotate.conf @@ -0,0 +1,32 @@ +# see "man logrotate" for details +# rotate log files weekly +daily + +# keep 2 days worth of backlogs +rotate 2 + +# create new (empty) log files after rotating old ones +create + +# uncomment this if you want your log files compressed +compress + +# packages drop log rotation information into this directory +include /etc/logrotate.d + +# no packages own wtmp, or btmp -- we'll rotate them here +/var/log/wtmp { + missingok + monthly + create 0664 root utmp + rotate 1 +} + +/var/log/btmp { + missingok + monthly + create 0660 root utmp + rotate 1 +} + +# system-specific logs may be configured here diff --git a/image/mkimg.sh b/image/mkimg.sh index 15b3c58f..6295a20a 100755 --- a/image/mkimg.sh +++ b/image/mkimg.sh @@ -61,6 +61,9 @@ chmod 755 mnt/usr/sbin/sdr-tool.sh #ping udev cp -f 99-uavionix.rules mnt/etc/udev/rules.d +#logrotate conf +cp -f logrotate.conf mnt/etc/logrotate.conf + #fan/temp control script #remove old script rm -rf mnt/usr/bin/fancontrol.py diff --git a/image/rc.local b/image/rc.local index e7e08161..3cc8b9f0 100755 --- a/image/rc.local +++ b/image/rc.local @@ -17,6 +17,13 @@ if [ "$_IP" ]; then printf "My IP address is %s\n" "$_IP" fi + +# +# Rotate logs on boot. +# +/usr/sbin/logrotate /etc/logrotate.conf + + /usr/bin/stratux-screen.py start route add default gw 10.26.36.1 diff --git a/main/gen_gdl90.go b/main/gen_gdl90.go index 06b1c203..d4ee896f 100644 --- a/main/gen_gdl90.go +++ b/main/gen_gdl90.go @@ -1172,12 +1172,25 @@ func addSystemError(err error) { globalStatus.Errors = append(globalStatus.Errors, err.Error()) } +var systemErrsMutex *sync.Mutex +var systemErrs map[string]string + +func addSingleSystemErrorf(ident string, format string, a ...interface{}) { + systemErrsMutex.Lock() + if _, ok := systemErrs[ident]; !ok { + // Error hasn't been thrown yet. + systemErrs[ident] = fmt.Sprintf(format, a...) + globalStatus.Errors = append(globalStatus.Errors, systemErrs[ident]) + log.Printf("Added critical system error: %s\n", systemErrs[ident]) + } + // Do nothing on this call if the error has already been thrown. + systemErrsMutex.Unlock() +} + func saveSettings() { fd, err := os.OpenFile(configLocation, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0644)) if err != nil { - err_ret := fmt.Errorf("can't save settings %s: %s", configLocation, err.Error()) - addSystemError(err_ret) - log.Printf("%s\n", err_ret.Error()) + addSingleSystemErrorf("save-settings", "can't save settings %s: %s", configLocation, err.Error()) return } defer fd.Close() @@ -1225,7 +1238,6 @@ func fsWriteTest(dir string) error { func printStats() { statTimer := time.NewTicker(30 * time.Second) - diskUsageWarning := false for { <-statTimer.C var memstats runtime.MemStats @@ -1250,10 +1262,8 @@ func printStats() { log.Printf("- " + strings.Join(sensorsOutput, ", ") + "\n") } // Check if we're using more than 95% of the free space. If so, throw a warning (only once). - if !diskUsageWarning && usage.Usage() > 0.95 { - err_p := fmt.Errorf("Disk bytes used = %s (%.1f %%), Disk bytes free = %s (%.1f %%)", humanize.Bytes(usage.Used()), 100*usage.Usage(), humanize.Bytes(usage.Free()), 100*(1-usage.Usage())) - addSystemError(err_p) - diskUsageWarning = true + if usage.Usage() > 0.95 { + addSingleSystemErrorf("disk-space", "Disk bytes used = %s (%.1f %%), Disk bytes free = %s (%.1f %%)", humanize.Bytes(usage.Used()), 100*usage.Usage(), humanize.Bytes(usage.Free()), 100*(1-usage.Usage())) } logStatus() } @@ -1385,6 +1395,10 @@ func main() { mySituation.muBaro = &sync.Mutex{} mySituation.muSatellite = &sync.Mutex{} + // Set up system error tracking. + systemErrsMutex = &sync.Mutex{} + systemErrs = make(map[string]string) + // Set up status. globalStatus.Version = stratuxVersion globalStatus.Build = stratuxBuild @@ -1410,13 +1424,11 @@ func main() { vtF, err := strconv.ParseFloat(vtS, 32) if err == nil { if vtF < 8.0 { - var err_os error if globalStatus.HardwareBuild == "FlightBox" { - err_os = fmt.Errorf("You are running an old Stratux image that can't be updated fully and is now deprecated. Visit https://www.openflightsolutions.com/flightbox/image-update-required for further information.") + addSingleSystemErrorf("deprecated-image", "You are running an old Stratux image that can't be updated fully and is now deprecated. Visit https://www.openflightsolutions.com/flightbox/image-update-required for further information.") } else { - err_os = fmt.Errorf("You are running an old Stratux image that can't be updated fully and is now deprecated. Visit http://stratux.me/ to update using the latest release image.") + addSingleSystemErrorf("deprecated-image", "You are running an old Stratux image that can't be updated fully and is now deprecated. Visit http://stratux.me/ to update using the latest release image.") } - addSystemError(err_os) } else { // Running Jessie or better. Remove some old init.d files. // This made its way in here because /etc/init.d/stratux invokes the update script, which can't delete the init.d file. @@ -1453,9 +1465,7 @@ func main() { // Duplicate log.* output to debugLog. fp, err := os.OpenFile(debugLogf, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { - err_log := fmt.Errorf("Failed to open '%s': %s", debugLogf, err.Error()) - addSystemError(err_log) - log.Printf("%s\n", err_log.Error()) + addSingleSystemErrorf(debugLogf, "Failed to open '%s': %s", debugLogf, err.Error()) } else { defer fp.Close() // Keep the logfile handle for later use diff --git a/main/managementinterface.go b/main/managementinterface.go index 607a8dad..a94cbde6 100644 --- a/main/managementinterface.go +++ b/main/managementinterface.go @@ -10,6 +10,7 @@ package main import ( + "archive/zip" "encoding/hex" "encoding/json" "fmt" @@ -21,13 +22,12 @@ import ( "net/http" "os" "os/exec" + "path/filepath" "regexp" "strings" "syscall" "text/template" "time" - "archive/zip" - "path/filepath" ) type SettingMessage struct { @@ -403,7 +403,7 @@ func handleDeleteAHRSLogFiles(w http.ResponseWriter, r *http.Request) { var fn string for _, f := range files { fn = f.Name() - if v, _ := filepath.Match("sensors_*.csv", fn) ; v { + if v, _ := filepath.Match("sensors_*.csv", fn); v { os.Remove("/var/log/" + fn) log.Printf("Deleting AHRS log file %s\n", fn) } @@ -654,14 +654,12 @@ func defaultServer(w http.ResponseWriter, r *http.Request) { func handleroPartitionRebuild(w http.ResponseWriter, r *http.Request) { out, err := exec.Command("/usr/sbin/rebuild_ro_part.sh").Output() - var ret_err error if err != nil { - ret_err = fmt.Errorf("Rebuild RO Partition error: %s", err.Error()) + addSingleSystemErrorf("partition-rebuild", "Rebuild RO Partition error: %s", err.Error()) } else { - ret_err = fmt.Errorf("Rebuild RO Partition success: %s", out) + addSingleSystemErrorf("partition-rebuild", "Rebuild RO Partition success: %s", out) } - addSystemError(ret_err) } // https://gist.github.com/alexisrobert/982674. diff --git a/main/network.go b/main/network.go index 17b8d742..93425ae2 100644 --- a/main/network.go +++ b/main/network.go @@ -10,8 +10,6 @@ package main import ( - "errors" - "fmt" "github.com/tarm/serial" "golang.org/x/net/icmp" "golang.org/x/net/ipv4" @@ -76,19 +74,16 @@ const ( extra_hosts_file = "/etc/stratux-static-hosts.conf" ) -var dhcpLeaseFileWarning bool var dhcpLeaseDirectoryLastTest time.Time // Last time fsWriteTest() was run on the DHCP lease directory. // Read the "dhcpd.leases" file and parse out IP/hostname. func getDHCPLeases() (map[string]string, error) { // Do a write test. Even if we are able to read the file, it may be out of date because there's a fs write issue. // Only perform the test once every 5 minutes to minimize writes. - if !dhcpLeaseFileWarning && (stratuxClock.Since(dhcpLeaseDirectoryLastTest) >= 5*time.Minute) { + if stratuxClock.Since(dhcpLeaseDirectoryLastTest) >= 5*time.Minute { err := fsWriteTest(dhcp_lease_dir) if err != nil { - err_p := fmt.Errorf("Write error on '%s', your EFB may have issues receiving weather and traffic.", dhcp_lease_dir) - addSystemError(err_p) - dhcpLeaseFileWarning = true + addSingleSystemErrorf("fs-write", "Write error on '%s', your EFB may have issues receiving weather and traffic.", dhcp_lease_dir) } dhcpLeaseDirectoryLastTest = stratuxClock.Time } @@ -602,8 +597,6 @@ func networkStatsCounter() { */ func ffMonitor() { - ff_warned := false // Has a warning been issued via globalStatus.Errors? - addr := net.UDPAddr{Port: 50113, IP: net.ParseIP("0.0.0.0")} conn, err := net.ListenUDP("udp", &addr) if err != nil { @@ -637,11 +630,7 @@ func ffMonitor() { if strings.HasPrefix(s, "i-want-to-play-ffm-udp") || strings.HasPrefix(s, "i-can-play-ffm-udp") || strings.HasPrefix(s, "i-cannot-play-ffm-udp") { p.FFCrippled = true //FIXME: AHRS output doesn't need to be disabled globally, just on the ForeFlight client IPs. - if !ff_warned { - e := errors.New("Stratux is not supported by your EFB app. Your EFB app is known to regularly make changes that cause compatibility issues with Stratux. See the README for a list of apps that officially support Stratux.") - addSystemError(e) - ff_warned = true - } + addSingleSystemErrorf("ff-warn", "Stratux is not supported by your EFB app. Your EFB app is known to regularly make changes that cause compatibility issues with Stratux. See the README for a list of apps that officially support Stratux.") } outSockets[ffIpAndPort] = p netMutex.Unlock() diff --git a/main/sdr.go b/main/sdr.go index f7d57b04..40fab42f 100644 --- a/main/sdr.go +++ b/main/sdr.go @@ -10,7 +10,6 @@ package main import ( - "fmt" "log" "os/exec" "regexp" @@ -484,8 +483,7 @@ func sdrWatcher() { } else if globalSettings.DeveloperMode { // Throw a "critical error" if developer mode is enabled. Alerts the developer that the daemon was restarted (possibly) // unexpectedly. - daemonRestartedErr := fmt.Errorf("System uptime %d seconds. Daemon was restarted.\n", info.Uptime) - addSystemError(daemonRestartedErr) + addSingleSystemErrorf("restart-warn", "System uptime %d seconds. Daemon was restarted.\n", info.Uptime) } } diff --git a/main/sensors.go b/main/sensors.go index f03d32ff..caa593b9 100644 --- a/main/sensors.go +++ b/main/sensors.go @@ -22,13 +22,13 @@ const ( ) var ( - i2cbus embd.I2CBus - myPressureReader sensors.PressureReader - myIMUReader sensors.IMUReader - cal chan (string) - analysisLogger *ahrs.AHRSLogger - ahrsCalibrating bool - logMap map[string]interface{} + i2cbus embd.I2CBus + myPressureReader sensors.PressureReader + myIMUReader sensors.IMUReader + cal chan (string) + analysisLogger *ahrs.AHRSLogger + ahrsCalibrating bool + logMap map[string]interface{} ) func initI2CSensors() { @@ -52,7 +52,6 @@ func pollSensors() { // If it's not currently connected, try connecting to IMU if globalSettings.IMU_Sensor_Enabled && !globalStatus.IMUConnected { - log.Println("AHRS Info: attempting IMU connection.") globalStatus.IMUConnected = initIMU() // I2C accel/gyro/mag. } } @@ -62,13 +61,11 @@ func initPressureSensor() (ok bool) { bmp, err := sensors.NewBMP280(&i2cbus, 100*time.Millisecond) if err == nil { myPressureReader = bmp - log.Println("AHRS Info: Successfully initialized BMP280") return true } //TODO westphae: make bmp180.go to fit bmp interface - log.Println("AHRS Info: couldn't initialize BMP280 or BMP180") return false } @@ -93,14 +90,14 @@ func tempAndPressureSender() { // Read temperature and pressure altitude. temp, err = myPressureReader.Temperature() if err != nil { - log.Printf("AHRS Error: Couldn't read temperature from sensor: %s", err) + addSingleSystemErrorf("pressure-sensor-temp-read", "AHRS Error: Couldn't read temperature from sensor: %s", err) } press, err = myPressureReader.Pressure() if err != nil { - log.Printf("AHRS Error: Couldn't read pressure from sensor: %s", err) + addSingleSystemErrorf("pressure-sensor-pressure-read", "AHRS Error: Couldn't read pressure from sensor: %s", err) failNum++ if failNum > numRetries { - log.Printf("AHRS Error: Couldn't read pressure from sensor %d times, closing BMP: %s", failNum, err) + // log.Printf("AHRS Error: Couldn't read pressure from sensor %d times, closing BMP: %s", failNum, err) myPressureReader.Close() globalStatus.BMPConnected = false // Try reconnecting a little later break @@ -129,16 +126,16 @@ func initIMU() (ok bool) { imu, err := sensors.NewMPU9250() if err == nil { myIMUReader = imu - log.Println("AHRS Info: Successfully connected MPU9250") return true } // TODO westphae: try to connect to MPU9150 or other IMUs. - log.Println("AHRS Error: couldn't initialize an IMU") return false } +//FIXME: Shoud be moved to managementinterface.go and standardized on management interface port. + func sensorAttitudeSender() { var ( t time.Time @@ -147,7 +144,6 @@ func sensorAttitudeSender() { failNum uint8 ) - log.Println("AHRS Info: initializing new Simple AHRS") s := ahrs.NewSimpleAHRS() m := ahrs.NewMeasurement() cal = make(chan (string), 1) @@ -155,9 +151,8 @@ func sensorAttitudeSender() { // Set up loggers for analysis ahrswebListener, err := ahrsweb.NewKalmanListener() if err != nil { - log.Printf("AHRS Info: couldn't start ahrswebListener: %s\n", err.Error()) + // addSingleSystemErrorf("ahrs-web-start", "AHRS Info: couldn't start ahrswebListener: %s\n", err.Error()) } else { - log.Println("AHRS Info: ahrswebListener started on port 8000") defer ahrswebListener.Close() } @@ -165,7 +160,7 @@ func sensorAttitudeSender() { timer := time.NewTicker(50 * time.Millisecond) // ~20Hz update. for { // Set sensor gyro calibrations - if c, d := &globalSettings.C, &globalSettings.D; d[0]*d[0] + d[1]*d[1] + d[2]*d[2] > 0 { + if c, d := &globalSettings.C, &globalSettings.D; d[0]*d[0]+d[1]*d[1]+d[2]*d[2] > 0 { s.SetCalibrations(c, d) log.Printf("AHRS Info: IMU Calibrations read from settings: accel %6f %6f %6f; gyro %6f %6f %6f\n", c[0], c[1], c[2], d[0], d[1], d[2]) @@ -178,7 +173,7 @@ func sensorAttitudeSender() { } // Set sensor quaternion - if f := &globalSettings.SensorQuaternion; f[0]*f[0] + f[1]*f[1] + f[2]*f[2] + f[3]*f[3] > 0 { + if f := &globalSettings.SensorQuaternion; f[0]*f[0]+f[1]*f[1]+f[2]*f[2]+f[3]*f[3] > 0 { s.SetSensorQuaternion(f) } else { select { // Don't block if cal isn't receiving: only need one calibration in the queue at a time. diff --git a/notes/app-vendor-integration.md b/notes/app-vendor-integration.md index bd41ad64..1be50138 100644 --- a/notes/app-vendor-integration.md +++ b/notes/app-vendor-integration.md @@ -55,7 +55,7 @@ Some 1090ES transponders will send the actual registration number of the aircraf a squawk code. -### Additional data available to EFBs +### Additional data/control available to EFBs Stratux makes available a webserver to retrieve statistics which may be useful to EFBs: @@ -262,3 +262,16 @@ Subsequent update (2837120 = 2B4A80 reports a newer position, altitude increased ```json {"Icao_addr":2837120,"OnGround":false,"Lat":42.193336,"Lng":-83.92136,"Position_valid":true,"Alt":3400,"Track":9,"Speed":92,"Speed_valid":true,"Vvel":0,"Tail":"","Last_seen":"2015-12-22T21:29:22.252914555Z","Last_source":2} ``` + +* `http://192.168.10.1/calibrateAHRS` - run AHRS sensor calibration routine. Submit a blank POST to this URL. + +* `http://192.168.10.1/cageAHRS` - "level" attitude display. Submit a blank POST to this URL. + +* `http://192.168.10.1/resetGMeter` - reset G-meter to zero. Submit a blank POST to this URL. + +* `http://192.168.10.1/restart` - restart Stratux application. + +* `http://192.168.10.1/reboot` - reboot the system. + +* `http://192.168.10.1/shutdown` - shutdown the system. + diff --git a/selfupdate/makeupdate.sh b/selfupdate/makeupdate.sh index 5f05bf02..8bbe3226 100755 --- a/selfupdate/makeupdate.sh +++ b/selfupdate/makeupdate.sh @@ -18,7 +18,6 @@ mkdir -p work/bin cp gen_gdl90 work/bin/ cp fancontrol work/bin/ cp libdump978.so work/bin/ -cp linux-mpu9150/libimu.so work/bin/ cp __lib__systemd__system__stratux.service work/bin/ cp __root__stratux-pre-start.sh work/bin/ cp dump1090/dump1090 work/bin/ @@ -39,10 +38,11 @@ cp image/stratux-wifi.sh work/bin/ cp image/rc.local work/bin/ cp image/dhcpd.conf work/bin/ cp image/interfaces work/bin/ +cp image/logrotate.conf work/bin + cp test-data/ahrs/ahrs_table.log work/bin/ cp ahrs_approx work/bin/ - #TODO: librtlsdr. cd work/ cat ../selfupdate/update_header.sh >update.sh diff --git a/selfupdate/update_footer.sh b/selfupdate/update_footer.sh index 6e21b53f..34c5a2cd 100755 --- a/selfupdate/update_footer.sh +++ b/selfupdate/update_footer.sh @@ -1,6 +1,5 @@ cp -f gen_gdl90 /usr/bin/gen_gdl90 cp -f libdump978.so /usr/lib/libdump978.so -cp -f libimu.so /usr/lib/libimu.so # Startup script. @@ -23,6 +22,9 @@ ln -fs /lib/systemd/system/stratux.service /etc/systemd/system/multi-user.target cp -f hostapd.conf /etc/hostapd/hostapd.conf cp -f hostapd-edimax.conf /etc/hostapd/hostapd-edimax.conf +#logrotate config +cp -f logrotate.conf /etc/logrotate.conf + #WiFi Hostapd ver test and hostapd.conf builder script cp -f stratux-wifi.sh /usr/sbin/ diff --git a/selfupdate/update_header.sh b/selfupdate/update_header.sh index ff758008..7c912f32 100755 --- a/selfupdate/update_header.sh +++ b/selfupdate/update_header.sh @@ -3,5 +3,4 @@ rm -rf /root/stratux-update mkdir -p /root/stratux-update cd /root/stratux-update -rm -f /var/log/stratux.sqlite /var/log/stratux.sqlite-wal /var/log/stratux.sqlite-shm -rm -f /var/log/stratux.log +rm -f /var/log/stratux* diff --git a/web/plates/settings.html b/web/plates/settings.html index f02cb072..d5202597 100644 --- a/web/plates/settings.html +++ b/web/plates/settings.html @@ -1,5 +1,110 @@