diff --git a/Makefile b/Makefile index dfc0862e..4c5fd059 100644 --- a/Makefile +++ b/Makefile @@ -33,38 +33,7 @@ test: make -C test www: - mkdir -p /var/www - mkdir -p /var/www/css - cp web/css/*.css /var/www/css - mkdir -p /var/www/js - cp web/js/main.js /var/www/js - cp web/js/addtohomescreen.min.js /var/www/js - cp web/js/j3di-all.min.js /var/www/js - mkdir -p /var/www/img - cp web/img/logo*.png /var/www/img - cp web/img/screen*.png /var/www/img - cp web/img/world.png /var/www/img - mkdir -p /var/www/maui - mkdir -p /var/www/maui/js - cp web/maui/js/angular-ui-router.min.js /var/www/maui/js - cp web/maui/js/mobile-angular-ui.min.js /var/www/maui/js - cp web/maui/js/angular.min.js /var/www/maui/js - cp web/maui/js/mobile-angular-ui.gestures.min.js /var/www/maui/js - cp web/maui/js/mobile-angular-ui.core.min.js /var/www/maui/js - mkdir -p /var/www/maui/css - cp web/maui/css/mobile-angular-ui-hover.min.css /var/www/maui/css - cp web/maui/css/mobile-angular-ui-desktop.min.css /var/www/maui/css - cp web/maui/css/mobile-angular-ui-base.min.css /var/www/maui/css - mkdir -p /var/www/maui/fonts - cp web/maui/fonts/fontawesome-webfont.woff /var/www/maui/fonts - mkdir -p /var/www/plates - cp web/plates/*.html /var/www/plates - mkdir -p /var/www/plates/js - cp web/plates/js/*.js /var/www/plates/js - cp web/index.html /var/www - cp web/stratux.appcache /var/www - # Mark the manifest with the git hash. - echo "# Stratux build: " `git log -n 1 --pretty=%H` >>/var/www/stratux.appcache + cd web && make install: cp -f gen_gdl90 /usr/bin/gen_gdl90 diff --git a/image/spindle/wheezy-stage4 b/image/spindle/wheezy-stage4 index 72f91716..53f4305a 100755 --- a/image/spindle/wheezy-stage4 +++ b/image/spindle/wheezy-stage4 @@ -146,11 +146,9 @@ cp -f dump1090/dump1090 /usr/bin/ mv /tmp/gen_gdl90 /usr/bin/gen_gdl90 chmod +x /usr/bin/gen_gdl90 -cp start_uat.sh /usr/bin/start_uat + +#Startup script. cp init.d-stratux /etc/init.d/stratux -cp start_stratux.sh /usr/sbin/stratux -chmod 755 /usr/bin/start_uat -chmod 755 /usr/sbin/stratux chmod 755 /etc/init.d/stratux ln -s /etc/init.d/stratux /etc/rc2.d/S01stratux ln -s /etc/init.d/stratux /etc/rc6.d/K01stratux diff --git a/main/gen_gdl90.go b/main/gen_gdl90.go index 44eea303..9a1c19a8 100644 --- a/main/gen_gdl90.go +++ b/main/gen_gdl90.go @@ -604,9 +604,25 @@ func heartBeatSender() { sendGDL90(makeHeartbeat(), false) sendGDL90(makeStratuxHeartbeat(), false) sendGDL90(makeStratuxStatus(), false) - // sendGDL90(makeTrafficReport()) makeOwnshipReport() makeOwnshipGeometricAltitudeReport() + /* --- debug code: traffic demo* --- / + /* Uncomment and compile to display huge number of artificial traffic targets + numTargets := uint32(1000) + hexCode := uint32(0xFF0000) + + for i := uint32(0); i < numTargets; i++ { + tail := fmt.Sprintf("DEMO%d", i) + alt := float32((i*117%2000)*25 + 2000) + hdg := float64((i * 37) % 360) + spd := float64(100 + ((i*7)%61)*13) + + updateDemoTraffic(i|hexCode, tail, alt, spd, hdg) + + } + + */ + /* ---end traffic demo code ---*/ sendTrafficUpdates() updateStatus() case <-timerMessageStats.C: @@ -1000,22 +1016,30 @@ type settings struct { } type status struct { - Version string - Devices uint32 - Connected_Users uint - UAT_messages_last_minute uint - uat_products_last_minute map[string]uint32 - UAT_messages_max uint - ES_messages_last_minute uint - ES_messages_max uint - GPS_satellites_locked uint16 - GPS_satellites_seen uint16 - GPS_satellites_tracked uint16 - GPS_connected bool - GPS_solution string - RY835AI_connected bool - Uptime int64 - CPUTemp float32 + Version string + Devices uint32 + Connected_Users uint + UAT_messages_last_minute uint + uat_products_last_minute map[string]uint32 + UAT_messages_max uint + ES_messages_last_minute uint + ES_messages_max uint + GPS_satellites_locked uint16 + GPS_satellites_seen uint16 + GPS_satellites_tracked uint16 + GPS_connected bool + GPS_solution string + RY835AI_connected bool + Uptime int64 + CPUTemp float32 + NetworkDataMessagesSent uint64 + NetworkDataMessagesSentNonqueueable uint64 + NetworkDataBytesSent uint64 + NetworkDataBytesSentNonqueueable uint64 + NetworkDataMessagesSentLastSec uint64 + NetworkDataMessagesSentNonqueueableLastSec uint64 + NetworkDataBytesSentLastSec uint64 + NetworkDataBytesSentNonqueueableLastSec uint64 } var globalSettings settings @@ -1135,6 +1159,7 @@ func printStats() { log.Printf("stats [started: %s]\n", humanize.RelTime(time.Time{}, stratuxClock.Time, "ago", "from now")) log.Printf(" - CPUTemp=%.02f deg C, MemStats.Alloc=%s, MemStats.Sys=%s, totalNetworkMessagesSent=%s\n", globalStatus.CPUTemp, humanize.Bytes(uint64(memstats.Alloc)), humanize.Bytes(uint64(memstats.Sys)), humanize.Comma(int64(totalNetworkMessagesSent))) log.Printf(" - UAT/min %s/%s [maxSS=%.02f%%], ES/min %s/%s\n, Total traffic targets tracked=%s", humanize.Comma(int64(globalStatus.UAT_messages_last_minute)), humanize.Comma(int64(globalStatus.UAT_messages_max)), float64(maxSignalStrength)/10.0, humanize.Comma(int64(globalStatus.ES_messages_last_minute)), humanize.Comma(int64(globalStatus.ES_messages_max)), humanize.Comma(int64(len(seenTraffic)))) + log.Printf(" - Network data messages sent: %d total, %d nonqueueable. Network data bytes sent: %d total, %d nonqueueable.\n", globalStatus.NetworkDataMessagesSent, globalStatus.NetworkDataMessagesSentNonqueueable, globalStatus.NetworkDataBytesSent, globalStatus.NetworkDataBytesSentNonqueueable) if globalSettings.GPS_Enabled { log.Printf(" - Last GPS fix: %s, GPS solution type: %d using %d satellites (%d/%d seen/tracked), NACp: %d, est accuracy %.02f m\n", stratuxClock.HumanizeTime(mySituation.LastFixLocalTime), mySituation.quality, mySituation.Satellites, mySituation.SatellitesSeen, mySituation.SatellitesTracked, mySituation.NACp, mySituation.Accuracy) log.Printf(" - GPS vertical velocity: %.02f ft/sec; GPS vertical accuracy: %v m\n", mySituation.GPSVertVel, mySituation.AccuracyVert) diff --git a/main/network.go b/main/network.go index d72ae658..1ac0a3d3 100644 --- a/main/network.go +++ b/main/network.go @@ -124,14 +124,20 @@ func sendToAllConnectedClients(msg networkMessage) { } // Send non-queueable messages immediately, or discard if the client is in sleep mode. - if sleepFlag { - continue + if !sleepFlag { + netconn.numOverflows = 0 // Reset the overflow counter whenever the client is not sleeping so that we're not penalizing future sleepmodes. } - netconn.numOverflows = 0 // Reset the overflow counter whenever the client is not sleeping so that we're not penalizing future sleepmodes. if !msg.queueable { + if sleepFlag { + continue + } netconn.Conn.Write(msg.msg) // Write immediately. totalNetworkMessagesSent++ + globalStatus.NetworkDataMessagesSent++ + globalStatus.NetworkDataMessagesSentNonqueueable++ + globalStatus.NetworkDataBytesSent += uint64(len(msg.msg)) + globalStatus.NetworkDataBytesSentNonqueueable += uint64(len(msg.msg)) } else { // Queue the message if the message is "queueable". if len(netconn.messageQueue) >= maxUserMsgQueueSize { // Too many messages queued? Drop the oldest. @@ -144,14 +150,22 @@ func sendToAllConnectedClients(msg networkMessage) { netconn.messageQueue = netconn.messageQueue[s:] } } - netconn.messageQueue = append(netconn.messageQueue, msg.msg) + netconn.messageQueue = append(netconn.messageQueue, msg.msg) // each netconn.messageQueue is therefore an array (well, a slice) of formatted GDL90 messages outSockets[k] = netconn } } } -// Just returns the number of DHCP leases for now. +// Returns the number of DHCP leases and prints queue lengths func getNetworkStats() uint { + for _, netconn := range outSockets { + queueBytes := 0 + for _, msg := range netconn.messageQueue { + queueBytes += len(msg) + } + log.Printf("On %s:%d, Queue length = %d messages / %d bytes\n", netconn.Ip, netconn.Port, len(netconn.messageQueue), queueBytes) + } + ret := uint(len(dhcpLeases)) globalStatus.Connected_Users = ret return ret @@ -217,11 +231,49 @@ func messageQueueSender() { if len(netconn.messageQueue) > 0 && !isSleeping(k) && !isThrottled(k) { averageSendableQueueSize += float64(len(netconn.messageQueue)) // Add num sendable messages. - tmpConn := netconn - tmpConn.Conn.Write(tmpConn.messageQueue[0]) + var queuedMsg []byte + + // Combine the first 256 entries in netconn.messageQueue to avoid flooding wlan0 with too many IOPS. + // Need to play nice with non-queued messages, so this limits the number of entries to combine. + // UAT uplink block is 432 bytes, so transmit block size shouldn't be larger than 108 KiB. 10 Mbps per device would therefore be needed to send within a 100 ms window. + + mqDepth := len(netconn.messageQueue) + if mqDepth > 256 { + mqDepth = 256 + } + + for j := 0; j < mqDepth; j++ { + queuedMsg = append(queuedMsg, netconn.messageQueue[j]...) + } + + /* + for j, _ := range netconn.messageQueue { + queuedMsg = append(queuedMsg, netconn.messageQueue[j]...) + } + */ + + netconn.Conn.Write(queuedMsg) totalNetworkMessagesSent++ - tmpConn.messageQueue = tmpConn.messageQueue[1:] - outSockets[k] = tmpConn + globalStatus.NetworkDataMessagesSent++ + globalStatus.NetworkDataBytesSent += uint64(len(queuedMsg)) + + //netconn.messageQueue = [][]byte{} + if mqDepth < len(netconn.messageQueue) { + netconn.messageQueue = netconn.messageQueue[mqDepth:] + } else { + netconn.messageQueue = [][]byte{} + } + outSockets[k] = netconn + + /* + tmpConn := netconn + tmpConn.Conn.Write(tmpConn.messageQueue[0]) + totalNetworkMessagesSent++ + globalStatus.NetworkDataMessagesSent++ + globalStatus.NetworkDataBytesSent += uint64(len(tmpConn.messageQueue[0])) + tmpConn.messageQueue = tmpConn.messageQueue[1:] + outSockets[k] = tmpConn + */ } } @@ -355,6 +407,29 @@ func sleepMonitor() { } } +func networkStatsCounter() { + timer := time.NewTicker(1 * time.Second) + var previousNetworkMessagesSent, previousNetworkBytesSent, previousNetworkMessagesSentNonqueueable, previousNetworkBytesSentNonqueueable uint64 + + for { + <-timer.C + globalStatus.NetworkDataMessagesSentLastSec = globalStatus.NetworkDataMessagesSent - previousNetworkMessagesSent + globalStatus.NetworkDataBytesSentLastSec = globalStatus.NetworkDataBytesSent - previousNetworkBytesSent + globalStatus.NetworkDataMessagesSentNonqueueableLastSec = globalStatus.NetworkDataMessagesSentNonqueueable - previousNetworkMessagesSentNonqueueable + globalStatus.NetworkDataBytesSentNonqueueableLastSec = globalStatus.NetworkDataBytesSentNonqueueable - previousNetworkBytesSentNonqueueable + + // debug option. Uncomment to log per-second network statistics. Useful for debugging WiFi instability. + //log.Printf("Network data messages sent: %d total, %d last second. Network data bytes sent: %d total, %d last second.\n", globalStatus.NetworkDataMessagesSent, globalStatus.NetworkDataMessagesSentLastSec, globalStatus.NetworkDataBytesSent, globalStatus.NetworkDataBytesSentLastSec) + + previousNetworkMessagesSent = globalStatus.NetworkDataMessagesSent + previousNetworkBytesSent = globalStatus.NetworkDataBytesSent + previousNetworkMessagesSentNonqueueable = globalStatus.NetworkDataMessagesSentNonqueueable + previousNetworkBytesSentNonqueueable = globalStatus.NetworkDataBytesSentNonqueueable + + } + +} + func initNetwork() { messageQueue = make(chan networkMessage, 1024) // Buffered channel, 1024 messages. outSockets = make(map[string]networkConnection) @@ -364,4 +439,5 @@ func initNetwork() { go monitorDHCPLeases() go messageQueueSender() go sleepMonitor() + go networkStatsCounter() } diff --git a/main/ry835ai.go b/main/ry835ai.go index 580cdd41..377af6cf 100644 --- a/main/ry835ai.go +++ b/main/ry835ai.go @@ -1114,7 +1114,7 @@ func attitudeReaderSender() { // Send, if valid. // if isGPSGroundTrackValid(), etc. - makeFFAHRSSimReport() + // makeFFAHRSSimReport() // simultaneous use of GDL90 and FFSIM not supported in FF 7.5.1 or later. Function definition will be kept for AHRS debugging and future workarounds. makeAHRSGDL90Report() mySituation.mu_Attitude.Unlock() diff --git a/main/traffic.go b/main/traffic.go index 476d37ca..5a679541 100644 --- a/main/traffic.go +++ b/main/traffic.go @@ -104,11 +104,16 @@ func sendTrafficUpdates() { trafficMutex.Lock() defer trafficMutex.Unlock() cleanupOldEntries() - for _, ti := range traffic { + var msg []byte + for _, ti := range traffic { // TO-DO: Limit number of aircraft in traffic message. ForeFlight chokes at ~1000-2000 messages depending on iDevice RAM. Practical limit likely around 500-1000 aircraft if ti.Position_valid { - makeTrafficReport(ti) + msg = append(msg, makeTrafficReportMsg(ti)...) } } + + if len(msg) > 0 { + sendGDL90(msg, false) + } } // Send update to attached client. @@ -121,7 +126,7 @@ func registerTrafficUpdate(ti TrafficInfo) { trafficUpdate.Send(tiJSON) } -func makeTrafficReport(ti TrafficInfo) { +func makeTrafficReportMsg(ti TrafficInfo) []byte { msg := make([]byte, 28) // See p.16. msg[0] = 0x14 // Message type "Traffic Report". @@ -200,7 +205,7 @@ func makeTrafficReport(ti TrafficInfo) { msg[19+i] = c } - sendGDL90(prepareMessage(msg), false) + return prepareMessage(msg) } func parseDownlinkReport(s string) { @@ -567,6 +572,56 @@ func esListen() { } } +/* +updateDemoTraffic creates / updates a simulated traffic target for demonstration / debugging +purpose. Target will circle clockwise around the current GPS position (if valid) or around +KOSH, once every five minutes. + +Inputs are ICAO 24-bit hex code, tail number (8 chars max), relative altitude in feet, +groundspeed in knots, and bearing offset from 0 deg initial position. +*/ +func updateDemoTraffic(icao uint32, tail string, relAlt float32, gs float64, offset float64) { + var ti TrafficInfo + + hdg := float64((stratuxClock.Milliseconds/1000)%360) + offset + // gs := float64(220) // knots + radius := gs * 0.1 / (2 * math.Pi) + x := radius * math.Cos(hdg*math.Pi/180.0) + y := radius * math.Sin(hdg*math.Pi/180.0) + // default traffic location is Oshkosh if GPS not detected + lat := 43.99 + lng := -88.56 + if isGPSValid() { + lat = float64(mySituation.Lat) + lng = float64(mySituation.Lng) + } + traffRelLat := y / 60 + traffRelLng := -x / (60 * math.Cos(lat*math.Pi/180.0)) + + ti.Icao_addr = icao + ti.OnGround = false + ti.addr_type = 0 + ti.emitter_category = 1 + ti.Lat = float32(lat + traffRelLat) + ti.Lng = float32(lng + traffRelLng) + ti.Position_valid = true + ti.Alt = int32(mySituation.Alt + relAlt) + ti.Track = uint16(hdg) + ti.Speed = uint16(gs) + ti.Speed_valid = true + ti.Vvel = 0 + ti.Tail = tail // "DEMO1234" + ti.Last_seen = stratuxClock.Time + ti.Last_source = 1 + + // now insert this into the traffic map... + trafficMutex.Lock() + defer trafficMutex.Unlock() + traffic[ti.Icao_addr] = ti + registerTrafficUpdate(ti) + seenTraffic[ti.Icao_addr] = true +} + func initTraffic() { traffic = make(map[uint32]TrafficInfo) seenTraffic = make(map[uint32]bool) diff --git a/selfupdate/makeupdate.sh b/selfupdate/makeupdate.sh new file mode 100755 index 00000000..447ea8e8 --- /dev/null +++ b/selfupdate/makeupdate.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +#apt-get install -y dh-make + +stratuxVersion=`git describe --tags --abbrev=0` +stratuxBuild=`git log -n 1 --pretty=%H` + +echo +echo +echo "Packaging ${stratuxVersion} (${stratuxBuild})." +echo +echo + +cd .. +make +rm -rf work +mkdir -p work/bin +cp gen_gdl90 work/bin/ +cp libdump978.so work/bin/ +cp linux-mpu9150/libimu.so work/bin/ +cp init.d-stratux work/bin/ +cp dump1090/dump1090 work/bin/ +#TODO: librtlsdr. +cd work/ +cat ../selfupdate/update_header.sh >update.sh +find bin/ -type f | while read fn; do + echo -n "packaging $fn... " + UPFN=`echo $fn | cut -d/ -f2` + echo "cat >${UPFN}.b64 <<__EOF__" >>update.sh + gzip -c $fn | base64 >>update.sh + echo "__EOF__" >>update.sh + echo "base64 -d ${UPFN}.b64 | gzip -d -c >${UPFN}" >>update.sh + echo "rm -f ${UPFN}.b64" >>update.sh + echo "done" +done +cat ../selfupdate/update_footer.sh >>update.sh + +chmod +x update.sh + +OUTF="update-${stratuxVersion}.sh" +mv update.sh $OUTF + + +echo +echo +echo "$OUTF ready." +echo +echo diff --git a/selfupdate/update_footer.sh b/selfupdate/update_footer.sh new file mode 100755 index 00000000..c67855a1 --- /dev/null +++ b/selfupdate/update_footer.sh @@ -0,0 +1,12 @@ +cp -f gen_gdl90 /usr/bin/gen_gdl90 +cp -f libdump978.so /usr/lib/libdump978.so +cp -f linux-mpu9150/libimu.so /usr/lib/libimu.so + + +#Startup script. +cp -f init.d-stratux /etc/init.d/stratux +chmod 755 /etc/init.d/stratux +ln -s /etc/init.d/stratux /etc/rc2.d/S01stratux +ln -s /etc/init.d/stratux /etc/rc6.d/K01stratux + +cp -f dump1090 /usr/bin/ diff --git a/selfupdate/update_header.sh b/selfupdate/update_header.sh new file mode 100755 index 00000000..a601ad20 --- /dev/null +++ b/selfupdate/update_header.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +rm -rf /root/stratux-update +mkdir -p /root/stratux-update +cd /root/stratux-update + diff --git a/start_stratux.sh b/start_stratux.sh deleted file mode 100755 index ac7a8b82..00000000 --- a/start_stratux.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -screen -S stratux -d -m /usr/bin/start_uat diff --git a/start_uat.sh b/start_uat.sh deleted file mode 100755 index 1b6dcb86..00000000 --- a/start_uat.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -/usr/bin/gen_gdl90 - diff --git a/web/Makefile b/web/Makefile new file mode 100644 index 00000000..2fe57de4 --- /dev/null +++ b/web/Makefile @@ -0,0 +1,33 @@ +all: + mkdir -p /var/www + mkdir -p /var/www/css + cp css/*.css /var/www/css + mkdir -p /var/www/js + cp js/main.js /var/www/js + cp js/addtohomescreen.min.js /var/www/js + cp js/j3di-all.min.js /var/www/js + mkdir -p /var/www/img + cp img/logo*.png /var/www/img + cp img/screen*.png /var/www/img + cp img/world.png /var/www/img + mkdir -p /var/www/maui + mkdir -p /var/www/maui/js + cp maui/js/angular-ui-router.min.js /var/www/maui/js + cp maui/js/mobile-angular-ui.min.js /var/www/maui/js + cp maui/js/angular.min.js /var/www/maui/js + cp maui/js/mobile-angular-ui.gestures.min.js /var/www/maui/js + cp maui/js/mobile-angular-ui.core.min.js /var/www/maui/js + mkdir -p /var/www/maui/css + cp maui/css/mobile-angular-ui-hover.min.css /var/www/maui/css + cp maui/css/mobile-angular-ui-desktop.min.css /var/www/maui/css + cp maui/css/mobile-angular-ui-base.min.css /var/www/maui/css + mkdir -p /var/www/maui/fonts + cp maui/fonts/fontawesome-webfont.woff /var/www/maui/fonts + mkdir -p /var/www/plates + cp plates/*.html /var/www/plates + mkdir -p /var/www/plates/js + cp plates/js/*.js /var/www/plates/js + cp index.html /var/www + cp stratux.appcache /var/www + # Mark the manifest with the git hash. + echo "# Stratux build: " `git log -n 1 --pretty=%H` >>/var/www/stratux.appcache \ No newline at end of file