From de387625523388a1f96c7f3301fdcc33268d6da5 Mon Sep 17 00:00:00 2001 From: PrincessPi3 Date: Tue, 3 Jun 2025 03:29:35 -0600 Subject: [PATCH] flutterfungus wooo --- .gitignore | 7 + Garfana-Visualization.json | 542 ++++++++++++++++++++++++ README.md | 130 ++++++ chrony_statistics.py | 103 +++++ chrony_statistics.sh | 9 + dump_configs.sh | 22 + full_status.sh | 26 ++ installer1.sh | 12 + installer2.sh | 37 ++ installer3.sh | 15 + installer4.sh | 36 ++ nuke_logs.sh | 48 +++ reconfig_full.sh | 44 ++ restart_services.sh | 9 + start_services.sh | 3 + status_services.sh | 3 + stop_services.sh | 3 + working-debug-level-conf/50-tty.rules | 3 + working-debug-level-conf/chrony.conf | 31 ++ working-debug-level-conf/gpsd | 14 + working-debug-level-conf/grafana.ini | 26 ++ working-debug-level-conf/influxdb.conf | 21 + working-debug-level-conf/telegraf.conf | 22 + working-normal-level-conf/50-tty.rules | 1 + working-normal-level-conf/chrony.conf | 31 ++ working-normal-level-conf/gpsd | 14 + working-normal-level-conf/grafana.ini | 26 ++ working-normal-level-conf/influxdb.conf | 22 + working-normal-level-conf/telegraf.conf | 22 + working-warn-level-conf/50-tty.rules | 1 + working-warn-level-conf/chrony.conf | 31 ++ working-warn-level-conf/gpsd | 14 + working-warn-level-conf/grafana.ini | 26 ++ working-warn-level-conf/influxdb.conf | 22 + working-warn-level-conf/telegraf.conf | 22 + 35 files changed, 1398 insertions(+) create mode 100644 .gitignore create mode 100644 Garfana-Visualization.json create mode 100644 README.md create mode 100644 chrony_statistics.py create mode 100644 chrony_statistics.sh create mode 100644 dump_configs.sh create mode 100644 full_status.sh create mode 100644 installer1.sh create mode 100644 installer2.sh create mode 100644 installer3.sh create mode 100644 installer4.sh create mode 100644 nuke_logs.sh create mode 100644 reconfig_full.sh create mode 100644 restart_services.sh create mode 100644 start_services.sh create mode 100644 status_services.sh create mode 100644 stop_services.sh create mode 100644 working-debug-level-conf/50-tty.rules create mode 100644 working-debug-level-conf/chrony.conf create mode 100644 working-debug-level-conf/gpsd create mode 100644 working-debug-level-conf/grafana.ini create mode 100644 working-debug-level-conf/influxdb.conf create mode 100644 working-debug-level-conf/telegraf.conf create mode 100644 working-normal-level-conf/50-tty.rules create mode 100644 working-normal-level-conf/chrony.conf create mode 100644 working-normal-level-conf/gpsd create mode 100644 working-normal-level-conf/grafana.ini create mode 100644 working-normal-level-conf/influxdb.conf create mode 100644 working-normal-level-conf/telegraf.conf create mode 100644 working-warn-level-conf/50-tty.rules create mode 100644 working-warn-level-conf/chrony.conf create mode 100644 working-warn-level-conf/gpsd create mode 100644 working-warn-level-conf/grafana.ini create mode 100644 working-warn-level-conf/influxdb.conf create mode 100644 working-warn-level-conf/telegraf.conf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c749c34 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +old +*.bak* +scratch.* +chrony_statistics.log +conf-* +log_backup_* +influxdata-archive.key \ No newline at end of file diff --git a/Garfana-Visualization.json b/Garfana-Visualization.json new file mode 100644 index 0000000..6a9f670 --- /dev/null +++ b/Garfana-Visualization.json @@ -0,0 +1,542 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": { + "type": "influxdb", + "uid": "cenh879ru0gzkc" + }, + "description": "Long term average of time offset", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic-by-name" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "displayName": "RMS Offset (Seconds)", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.0.1", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "cenh879ru0gzkc" + }, + "groupBy": [ + { + "params": [ + "1m" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "hide": false, + "measurement": "chrony", + "orderByTime": "ASC", + "policy": "autogen", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "rms_offset" + ], + "type": "field" + }, + { + "params": [], + "type": "mean" + } + ] + ], + "tags": [] + } + ], + "title": "RMS Offset", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "cenh879ru0gzkc" + }, + "description": "Average chrony time offset over last minute in seconds", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic-by-name" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "displayName": "Offset (S)", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.0.1", + "targets": [ + { + "groupBy": [ + { + "params": [ + "1m" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "chrony", + "orderByTime": "ASC", + "policy": "autogen", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "last_offset" + ], + "type": "field" + }, + { + "params": [], + "type": "mean" + } + ] + ], + "tags": [] + } + ], + "title": "Last Time Offset (Seconds)", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "cenh879ru0gzkc" + }, + "description": "Chrony frequency offset over last minute in PPM", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic-by-name" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "displayName": "Frequency Offset (PPM)", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ppm" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 16 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.0.1", + "targets": [ + { + "groupBy": [ + { + "params": [ + "1m" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "chrony", + "orderByTime": "ASC", + "policy": "autogen", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "frequency" + ], + "type": "field" + }, + { + "params": [], + "type": "mean" + } + ] + ], + "tags": [] + } + ], + "title": "Frequency Offset (PPM)", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "cenh879ru0gzkc" + }, + "description": "Average chrony skew over past minute", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic-by-name" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "displayName": "Skew (PPM)", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ppm" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.0.1", + "targets": [ + { + "groupBy": [ + { + "params": [ + "1m" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "chrony", + "orderByTime": "ASC", + "policy": "autogen", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "skew" + ], + "type": "field" + }, + { + "params": [], + "type": "mean" + } + ] + ], + "tags": [] + } + ], + "title": "Frequency Skew (PPM)", + "type": "timeseries" + } + ], + "preload": false, + "refresh": "auto", + "schemaVersion": 41, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-3h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Chrony", + "uid": "69a006d6-ccaa-4e9e-a8ec-6dba0b30fa1e", + "version": 21 +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..570b10a --- /dev/null +++ b/README.md @@ -0,0 +1,130 @@ +# Precision Timekeeping Fuckery + +## How it works +### Services +* `gpsd` +* `chrony` +* `telegraf` +* `influxdb` +* `grafana-server` +* `syslog-ng` + +### Testing +**Status (Everything)** +* `bash ~/Precision-Timekeeping-Fuckery/full_status.sh` + +**Status (Services)** +* `bash ~/Precision-Timekeeping-Fuckert/status_services.sh` + +**Test PPS** +* `sudo ppstest /dev/pps0` + + +### Logs Stuff +**Chrony Logs** +* `sudo tail -f /var/log/chrony/tracking.log` +* `sudo tail -f /var/log/chrony/statistics.log` +* `sudo tail -f /var/log/chrony/measurements.log` + +**Telegraf Logs** +* `sudo tail -f /var/log/telegraf/telegraf.log` + +**Grafana Logs** +* `sudo tail -f /var/log/grafana/grafana.log` + +**Influxdb Logs** +* `sudo tail -f /var/log/syslog` + +## Setup +### Requirements +1. Raspberry Pi with + 1. Raspberry Pi OS Installed + 2. SSH Access + 3. sudo permissions +2. A timing specific GPS module, wired to the Raspberry Pi GPIOs: + 1. GPS GND to RPi pin 6 + 2. GPS VIN to RPi pin 2 or 4 + 3. GPS PPS to RPi pin 12 (GPIO 18) + 4. GPS RX to RPi pin 8 + 5. GPS TX to RPi pin 10 + +### Install +Clone the repo +`git clone https://github.com/PrincessPi3/Precision-Timekeeping-Fuckery.git ~/Precision-Timekeping-Fuckery` + +Run raspi-config +1. Interface Options->Serial Port + 1. Would you like a login shell to be accessible over serial? **** + 2. Would you like the serial port hardware to be enabled? **** +2. Advanced Options + 1. A1 Expand Filesystem + +update os and packages (will reboot when finished) +`bash ~/Precision-Timekeeping-Fuckery/update.sh` + +install packages (will reboot when finished) +`bash ~/Precision-Timekeeping-Fuckery/packages.sh` + +finish install (will reboot when finished) +`bash ~/Precision-Timekeeping-Fuckery/installer.sh` + +check status of everything +`bash ~/Precision-Timekeeping-Fuckery/full_status.sh` + +navigate to `http://:3000` +Upload ~/Garfana-Visualization.json to create dashboard + +### Clients +**Windows** +1. Open the Control Panel and navigate to the Date and Time settings. +2. Click on "Internet Time" and then "Change settings". +3. Enable the option to "Synchronize with an Internet time server". +4. Choose a time server from the available list or enter a custom one. +5. Click "OK" to save the changes. + +## Links +**Helpful Guide** https://austinsnerdythings.com/2025/02/14/revisiting-microsecond-accurate-ntp-for-raspberry-pi-with-gps-pps-in-2025/ + +**Current Module** +* Chipset https://www.u-blox.com/en/product/neo-f10n-module +* Breakout https://docs.sparkfun.com/SparkFun_u-blox_NEO-F10N/single_page/ + +**Next Module** +* Chipset https://www.u-blox.com/en/product/neolea-m8t-series +* Breakout https://www.ebay.com/itm/134243322249 + +**U-Blox Software (U-Center)** https://www.u-blox.com/en/product/u-center + + +**todo** +* x ntp server + * outside dial in + * cloudflare tunnel? +* change gps settings + * higher baudrate + 1. 230400 + 2. 460800 + 3. 921600 +* uninstall desktop/etc + * change boot target to cli +* README + * installer1-4.sh + * grafana + * url + * default login + * import + * data source + * fix + +**known bugs** +doubling of appending to /boot/firmaware/config.txt and /etc/modules when running intstaller3.sh + +notes: + reboots + grafana + connections->data sources + add influxdb + new dashboard + import + edit each panel and select influxdb as data source if issues + doubling of appending to /boot/firmaware/config.txt and /etc/modules \ No newline at end of file diff --git a/chrony_statistics.py b/chrony_statistics.py new file mode 100644 index 0000000..7cc543d --- /dev/null +++ b/chrony_statistics.py @@ -0,0 +1,103 @@ +# stolen from https://austinsnerdythings.com/2025/02/14/revisiting-microsecond-accurate-ntp-for-raspberry-pi-with-gps-pps-in-2025/ +import pandas as pd +import matplotlib.pyplot as plt +from datetime import datetime + +def parse_chrony_stats(file_path): + """ + Parse chrony statistics log file and return a pandas DataFrame + """ + # read file contents first + with open(file_path, 'r') as f: + file_contents = f.readlines() + + # for each line, if it starts with '=' or ' ', skip it + file_contents = [line for line in file_contents if not line.startswith('=') and not line.startswith(' ')] + + # exclude lines that include 'PPS' + file_contents = [line for line in file_contents if 'PPS' not in line] + + # Use StringIO to create a file-like object from the filtered contents + from io import StringIO + csv_data = StringIO(''.join(file_contents)) + + # Read the filtered data using pandas + df = pd.read_csv(csv_data, + delim_whitespace=True, + names=['Date', 'Time', 'IP_Address', 'Std_dev', 'Est_offset', 'Offset_sd', + 'Diff_freq', 'Est_skew', 'Stress', 'Ns', 'Bs', 'Nr', 'Asym']) + + + # Combine Date and Time columns into a datetime column + df['timestamp'] = pd.to_datetime(df['Date'] + ' ' + df['Time']) + + return df + +def plot_est_offset(df): + """ + Create a plot of Est_offset vs time for each IP address + """ + plt.figure(figsize=(12, 6)) + + # Plot each IP address as a separate series + for ip in df['IP_Address'].unique(): + ip_data = df[df['IP_Address'] == ip] + plt.plot(ip_data['timestamp'], ip_data['Est_offset'], + marker='o', label=ip, linestyle='-', markersize=4) + + plt.xlabel('Time') + plt.ylabel('Estimated Offset (seconds)') + plt.title('Chrony Estimated Offset Over Time by IP Address') + plt.legend() + plt.grid(True) + + # Rotate x-axis labels for better readability + plt.xticks(rotation=45) + + # Adjust layout to prevent label cutoff + plt.tight_layout() + + return plt + +def analyze_chrony_stats(file_path): + """ + Main function to analyze chrony statistics + """ + # Parse the data + df = parse_chrony_stats(file_path) + + # Create summary statistics + summary = { + 'IP Addresses': df['IP_Address'].nunique(), + 'Time Range': f"{df['timestamp'].min()} to {df['timestamp'].max()}", + 'Average Est Offset by IP': df.groupby('IP_Address')['Est_offset'].mean().to_dict(), + 'Max Est Offset by IP': df.groupby('IP_Address')['Est_offset'].max().to_dict(), + 'Min Est Offset by IP': df.groupby('IP_Address')['Est_offset'].min().to_dict(), + 'Median Est Offset by IP': df.groupby('IP_Address')['Est_offset'].median().to_dict() + } + + # Create the plot + plot = plot_est_offset(df) + + return df, summary, plot + +# Example usage +if __name__ == "__main__": + file_path = "chrony_statistics.log" # Replace with your file path + df, summary, plot = analyze_chrony_stats(file_path) + + # Print summary statistics + print("\nChrony Statistics Summary:") + print("-" * 30) + print(f"Number of IP Addresses: {summary['IP Addresses']}") + print(f"Time Range: {summary['Time Range']}") + print("\nAverage Estimated Offset by IP:") + for ip, avg in summary['Average Est Offset by IP'].items(): + print(f"{ip}: {avg:.2e}") + + print("\nMedian Estimated Offset by IP:") + for ip, median in summary['Median Est Offset by IP'].items(): + print(f"{ip}: {median:.2e}") + + # Show the plot + plt.show() \ No newline at end of file diff --git a/chrony_statistics.sh b/chrony_statistics.sh new file mode 100644 index 0000000..3341433 --- /dev/null +++ b/chrony_statistics.sh @@ -0,0 +1,9 @@ +#!/bin/bash +numlogs=500 +tmp_log="./chrony_statistics.log" + +sudo tail -n $numlogs /var/log/chrony/statistics.log > $tmp_log +sudo chown $USER:$USER $tmp_log +echo "$(wc -l $tmp_log) logs entered" +python chrony_statistics.py +rm -f $tmp_log \ No newline at end of file diff --git a/dump_configs.sh b/dump_configs.sh new file mode 100644 index 0000000..04fd393 --- /dev/null +++ b/dump_configs.sh @@ -0,0 +1,22 @@ +#!/bin/bash +set -e + +if [ -z $SUDO_USER ]; then + username=$SUDO_USER +else + username=$USER +fi + +dname=./conf-$(date +%s) +mkdir $dname + +sudo cp /etc/default/gpsd $dname/gpsd +sudo cp /etc/chrony/chrony.conf $dname/chrony.conf +sudo cp /etc/grafana/grafana.ini $dname/grafana.ini +sudo cp /etc/influxdb/influxdb.conf $dname/influxdb.conf +sudo cp /etc/telegraf/telegraf.conf $dname/telegraf.conf +sudo cp /etc/udev/rules.d/50-tty.rules $dname/50-tty.rules + +sudo chown -R $username:$username $dname +sudo chmod 775 $dname +sudo chmod 664 $dname/* \ No newline at end of file diff --git a/full_status.sh b/full_status.sh new file mode 100644 index 0000000..06477b6 --- /dev/null +++ b/full_status.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# status of services +bash ./status_services + +# test pps +echo "Testing PPS" +sudo ppstest /dev/pps0 + +# check gps +echo "Checking Normal GPS" +cgps + +# check gpsmon +echo "Checking Timekeeping GPS" +gpsmon + +# chek chrony sources +# chronyc sources + +# do same with watch +echo "Watching chronyc sources" +watch chronyc sources + +# track +echo "Watching chronyc tracking" +watch -n 10 chronyc tracking \ No newline at end of file diff --git a/installer1.sh b/installer1.sh new file mode 100644 index 0000000..17c68b9 --- /dev/null +++ b/installer1.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -e + +# updoot this +git pull + +# updoot +sudo apt update +sudo apt dist-upgrade -y +sudo apt autoremove + +sudo shutdown -r +1 \ No newline at end of file diff --git a/installer2.sh b/installer2.sh new file mode 100644 index 0000000..75afd39 --- /dev/null +++ b/installer2.sh @@ -0,0 +1,37 @@ +#!/bin/bash +set -e + +# sudo apt update + +# change boot target to cli before deleting desktop environ +# sudo systemctl set-default multi-user.target + +# nuke old packages +# sudo apt purge cups cups-browsed pulseaudio* bluetooth libgl* xserver* lightdm* raspberrypi-ui-mods vlc* lxde* chromium* desktop* gnome* gstreamer* gtk* hicolor-icon-theme* lx* mesa* -y + +# sudo apt dist-upgrade -y + +# install packages +sudo apt install influxdb pps-tools gpsd gpsd-clients chrony syslog-ng -y + +# grafana repo and install +sudo mkdir -p /etc/apt/keyrings/ +wget -q -O - https://apt.grafana.com/gpg.key | gpg --dearmor | sudo tee /etc/apt/keyrings/grafana.gpg > /dev/null +echo "deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com stable main" | sudo tee /etc/apt/sources.list.d/grafana.list + +# telegraf repo and install +curl --silent --location -O \ +https://repos.influxdata.com/influxdata-archive.key \ +&& echo "943666881a1b8d9b849b74caebf02d3465d6beb716510d86a39f6c8e8dac7515 influxdata-archive.key" \ +| sha256sum -c - && cat influxdata-archive.key \ +| gpg --dearmor \ +| sudo tee /etc/apt/trusted.gpg.d/influxdata-archive.gpg > /dev/null \ +&& echo 'deb [signed-by=/etc/apt/trusted.gpg.d/influxdata-archive.gpg] https://repos.influxdata.com/debian stable main' \ +| sudo tee /etc/apt/sources.list.d/influxdata.list + +sudo apt update +sudo apt install telegraf grafana -y + +sudo apt autoremove # cleanup + +sudo reboot \ No newline at end of file diff --git a/installer3.sh b/installer3.sh new file mode 100644 index 0000000..c3fb2f8 --- /dev/null +++ b/installer3.sh @@ -0,0 +1,15 @@ +#!/bin/bash +set -e # die on any error + +# configure the overlay +echo "Adding lines to /boot/firmware/config.txt to enable pps and gpio uart" +sudo bash -c "echo '# the next 3 lines are for GPS PPS signals' >> /boot/firmware/config.txt" +sudo bash -c "echo 'dtoverlay=pps-gpio,gpiopin=18' >> /boot/firmware/config.txt" +sudo bash -c "echo 'enable_uart=1' >> /boot/firmware/config.txt" +sudo bash -c "echo 'init_uart_baud=115200' >> /boot/firmware/config.txt" # set baudrate here too + +# add pps-gpio to modules +echo "Adding pps-gpio to modules" +sudo bash -c "echo 'pps-gpio' >> /etc/modules" + +sudo shutdown -r +1 \ No newline at end of file diff --git a/installer4.sh b/installer4.sh new file mode 100644 index 0000000..1323dce --- /dev/null +++ b/installer4.sh @@ -0,0 +1,36 @@ +#!/bin/bash +set -e +if [ ! -z $SUDO_USER ]; then + username=$SUDO_USER +else + username=$USER +fi + +# handle users serial shit +## self +echo "Giving your user the right permissions" +sudo usermod -aG tty $username +sudo usermod -aG root $username +sudo usermod -aG dialout $username +sudo usermod -aG plugdev $username +## service users +echo "Giving service users the right permissions" +sudo usermod -aG dialout gpsd +sudo usermod -aG dialout _chrony + +# reconfigure to normal mode +echo "Starting configure script" +bash ./reconfig_full.sh ./working-normal-level-conf + +# enable services +echo "Enabling Services" +sudo systemctl enable gpsd +sudo systemctl enable chrony +sudo systemctl enable influxdb +sudo systemctl enable telegraf +sudo systemctl enable grafana-server +sudo systemctl enable syslog-ng + +# reboot rq +echo "Rebooting in one minute" +sudo shutdown -r +1 \ No newline at end of file diff --git a/nuke_logs.sh b/nuke_logs.sh new file mode 100644 index 0000000..6179793 --- /dev/null +++ b/nuke_logs.sh @@ -0,0 +1,48 @@ +#!/bin/bash +set -e # die on any error + +# log locations +chrony_tracking=/var/log/chrony/tracking.log +chrony_statistics=/var/log/chrony/statistics.log +chrony_measurements=/var/log/chrony/measurements.log +telegraf=/var/log/telegraf/telegraf.log +grafana=/var/log/grafana/grafana.log +syslog=/var/log/syslog + +function backup_logs() { + # make log backup dir with unix timestamp + dname="./log_backup_$(date +%s)" + mkdir $dname + + # copy em + sudo cp $chrony_tracking $dname/chrony_tracking.log + sudo cp $chrony_statistics $dname/chrony_statistics.log + sudo cp $chrony_measurements $dname/chrony_measurements.log + sudo cp $telegraf $dname/telegraf.log + sudo cp $grafana $dnamme/grafana.log + sudo cp $syslog $dname/syslog + + # fix permissions + sudo chown -R $USER:$USER $dname + chmod 775 $dname + chmod 664 $dname/* + + # compress logs + tar fczvv $dname.tar.gz $dname + + # cleanup + rm -rf $dname +} + +function clear_logs() { + # truncate logs to empty files + sudo bash -c "echo '' > $chrony_tracking" + sudo bash -c "echo '' > $chrony_statistics" + sudo bash -c "echo '' > $chrony_measurements" + sudo bash -c "echo '' > $telegraf" + sudo bash -c "echo '' > $grafana" + # sudo bash -c "echo '' > $syslog" # leaving syslog alone for now +} + +backup_logs +clear_logs \ No newline at end of file diff --git a/reconfig_full.sh b/reconfig_full.sh new file mode 100644 index 0000000..aae082a --- /dev/null +++ b/reconfig_full.sh @@ -0,0 +1,44 @@ +#!/bin/bash +set -e # dir on any error + +# make sure dir works +if [ -z $1 ] || [ ! -d "$1" ]; then + echo "usage bash reconfig_full.sh /path/to/config/dir" + exit +fi + +# paths +## place in system +gpsd="/etc/default/gpsd" +chrony="/etc/chrony/conf.d/precision_timekeeping.conf" +grafana="/etc/grafana/grafana.ini" +influxdb="/etc/influxdb/influxdb.conf" +telegraf="/etc/telegraf/telegraf.conf" +udev_rule="/etc/udev/rules.d/50-tty.rules" +## new conf file paths +gpsd_new=""$1/gpsd"" +chrony_new="$1/chrony.conf" +grafana_new="$1/grafana.ini" +influxdb_new="$1/influxdb.conf" +telegraf_new="$1/telegraf.conf" +udev_new="$1/50-tty.rules" + +# stop da services +bash ./stop_services.sh + +# backup conf +bash ./dump_configs.sh + +# replace dem by truncation +sudo bash -c "cat $gpsd_new > $gpsd" +sudo bash -c "cat $chrony_new > $chrony" +sudo bash -c "cat $grafana_new > $grafana" +sudo bash -c "cat $influxdb_new > $influxdb" +sudo bash -c "cat $telegraf_new > $telegraf" +sudo bash -c "cat $udev_new > $udev_rule" + +# restart da services +bash ./start_services.sh + +# show services status +# bash ./status_services.sh \ No newline at end of file diff --git a/restart_services.sh b/restart_services.sh new file mode 100644 index 0000000..bc7c77f --- /dev/null +++ b/restart_services.sh @@ -0,0 +1,9 @@ +#!/bin/bash +echo "Restarting Services" +sudo systemctl restart gpsd chrony influxdb telegraf grafana-server syslog-ng +# sudo systemctl restart gpsd +# sudo systemctl restart chrony +# sudo systemctl restart influxdb +# sudo systemctl restart telegraf +# sudo systemctl restart grafana-server +# sudo systemctl restart syslog-ng \ No newline at end of file diff --git a/start_services.sh b/start_services.sh new file mode 100644 index 0000000..5e925ea --- /dev/null +++ b/start_services.sh @@ -0,0 +1,3 @@ +#!/bin/bash +echo "Starting Services" +sudo systemctl start gpsd chrony influxdb telegraf grafana-server syslog-ng \ No newline at end of file diff --git a/status_services.sh b/status_services.sh new file mode 100644 index 0000000..9ca2c1d --- /dev/null +++ b/status_services.sh @@ -0,0 +1,3 @@ +#!/bin/bash +echo "Services Status" +sudo systemctl status gpsd chrony influxdb telegraf grafana-server syslog-ng diff --git a/stop_services.sh b/stop_services.sh new file mode 100644 index 0000000..f6a6d00 --- /dev/null +++ b/stop_services.sh @@ -0,0 +1,3 @@ +#!/bin/bash +echo "Stopping Services" +sudo systemctl stop gpsd chrony influxdb telegraf grafana-server syslog-ng \ No newline at end of file diff --git a/working-debug-level-conf/50-tty.rules b/working-debug-level-conf/50-tty.rules new file mode 100644 index 0000000..f0562c3 --- /dev/null +++ b/working-debug-level-conf/50-tty.rules @@ -0,0 +1,3 @@ +SUBSYSTEM=="pps[0-9]*", GROUP="dialout", MODE="0660" +SUBSYSTEM=="tty[0-9]*", GROUP="dialout", MODE="0660" +SUBSYSTEM=="tty[A-Z]*[0-9]*", GROUP="dialout", MODE="0660" \ No newline at end of file diff --git a/working-debug-level-conf/chrony.conf b/working-debug-level-conf/chrony.conf new file mode 100644 index 0000000..c8013d3 --- /dev/null +++ b/working-debug-level-conf/chrony.conf @@ -0,0 +1,31 @@ +# SHM refclock is shared memory driver, it is populated by GPSd and read by chrony +# it is SHM 0 +# refid is what we want to call this source = NMEA +# offset = 0.000 means we do not yet know the delay +# precision is how precise this is. not 1e-3 = 1 millisecond, so not very precision +# poll 0 means poll every 2^0 seconds = 1 second poll interval +# filter 3 means take the average/median (forget which) of the 3 most recent readings. NMEA can be jumpy so we're averaging here +refclock SHM 0 refid NMEA offset 0.000 precision 1e-3 poll 0 filter 3 + +# PPS refclock is PPS specific, with /dev/pps0 being the source +# refid PPS means call it the PPS source +# lock NMEA means this PPS source will also lock to the NMEA source for time of day info +# offset = 0.0 means no offset... this should probably always remain 0 +# poll 3 = poll every 2^3=8 seconds. polling more frequently isn't necessarily better +# trust means we trust this time. the NMEA will be kicked out as false ticker eventually, so we need to trust the combo +refclock PPS /dev/pps0 refid PPS lock NMEA offset 0.0 poll 3 trust + +# also enable logging by uncommenting the logging line +log tracking measurements statistics + +# allow local connections to ntp +allow 10.0.0.0/24 + +# manual +manual + +# tell clients im the big dog +local stratum 1 + +# Good Time Servers +pool time.cloudflare.com iburst diff --git a/working-debug-level-conf/gpsd b/working-debug-level-conf/gpsd new file mode 100644 index 0000000..afd5b95 --- /dev/null +++ b/working-debug-level-conf/gpsd @@ -0,0 +1,14 @@ +# USB might be /dev/ttyACM0 +# serial might be /dev/ttyS0 +# pps might be /dev/pps0 +DEVICES="/dev/ttyS0 /dev/pps0" + +# -n means start without a client connection (i.e. at boot) +# -s 115200 is to set baud to 115200 +GPSD_OPTIONS="-n -s 115200" + +# also start in general +START_DAEMON="true" + +# Automatically hot add/remove USB GPS devices via gpsdctl +USBAUTO="true" diff --git a/working-debug-level-conf/grafana.ini b/working-debug-level-conf/grafana.ini new file mode 100644 index 0000000..72d5cb4 --- /dev/null +++ b/working-debug-level-conf/grafana.ini @@ -0,0 +1,26 @@ +instance_name = "Precision Timekeeping" + +[server] +# Protocol (http, https, h2, socket) +protocol = http + +# The http port to use +http_port = 3000 + +[log] +# Either "debug", "info", "warn", "error", "critical", default is "info" +level = debug + +# Either "console", "file", "syslog". Default is console and file +# Use space to separate multiple modes, e.g. "console file" +mode = console file + +[log.file] +# log line format, valid options are text, console and json +format = text + +# This enables automated log rotate(switch of following options), default is true +log_rotate = true + +# Max line number of single file, default is 1000000 +max_lines = 1000000 \ No newline at end of file diff --git a/working-debug-level-conf/influxdb.conf b/working-debug-level-conf/influxdb.conf new file mode 100644 index 0000000..1085819 --- /dev/null +++ b/working-debug-level-conf/influxdb.conf @@ -0,0 +1,21 @@ +# report usage statistics to influxdb company +reporting-enabled = false + +[meta] + # Where the metadata/raft database is stored + dir = "/var/lib/influxdb/meta" + +[data] + # The directory where the TSM storage engine stores TSM files. + dir = "/var/lib/influxdb/data" + + # The directory where the TSM storage engine stores WAL files. + wal-dir = "/var/lib/influxdb/wal" + + # Trace logging provides more verbose output around the tsm engine. Turning + # this on can provide more useful output for debugging tsm engine issues. + trace-logging-enabled = false + +[logging] + format = "auto" + level = "debug" \ No newline at end of file diff --git a/working-debug-level-conf/telegraf.conf b/working-debug-level-conf/telegraf.conf new file mode 100644 index 0000000..3b5f9f0 --- /dev/null +++ b/working-debug-level-conf/telegraf.conf @@ -0,0 +1,22 @@ +[global_tags] +[agent] + interval = "10s" + round_interval = true + metric_batch_size = 10000 + metric_buffer_limit = 1000000 + collection_jitter = "0s" + flush_interval = "10s" + flush_jitter = "0s" + precision = "0s" + logfile = "/var/log/telegraf/telegraf.log" + debug = true + +[[inputs.chrony]] + metrics = ["tracking"] + timeout = "5s" + +[[outputs.influxdb]] + urls = ["http://127.0.0.1:8086"] + database = "Chrony_Stats" + timeout = "5s" + # skip_database_creation = true diff --git a/working-normal-level-conf/50-tty.rules b/working-normal-level-conf/50-tty.rules new file mode 100644 index 0000000..9e6965d --- /dev/null +++ b/working-normal-level-conf/50-tty.rules @@ -0,0 +1 @@ +SUBSYSTEM=="pps*", GROUP="dialout", MODE="0660" \ No newline at end of file diff --git a/working-normal-level-conf/chrony.conf b/working-normal-level-conf/chrony.conf new file mode 100644 index 0000000..3028544 --- /dev/null +++ b/working-normal-level-conf/chrony.conf @@ -0,0 +1,31 @@ +# SHM refclock is shared memory driver, it is populated by GPSd and read by chrony +# it is SHM 0 +# refid is what we want to call this source = NMEA +# offset = 0.000 means we do not yet know the delay +# precision is how precise this is. not 1e-3 = 1 millisecond, so not very precision +# poll 0 means poll every 2^0 seconds = 1 second poll interval +# filter 3 means take the average/median (forget which) of the 3 most recent readings. NMEA can be jumpy so we're averaging here +refclock SHM 0 refid NMEA offset 0.000 precision 1e-3 poll 0 filter 3 + +# PPS refclock is PPS specific, with /dev/pps0 being the source +# refid PPS means call it the PPS source +# lock NMEA means this PPS source will also lock to the NMEA source for time of day info +# offset = 0.0 means no offset... this should probably always remain 0 +# poll 3 = poll every 2^3=8 seconds. polling more frequently isn't necessarily better +# trust means we trust this time. the NMEA will be kicked out as false ticker eventually, so we need to trust the combo +refclock PPS /dev/pps0 refid PPS lock NMEA offset 0.0 poll 3 trust + +# also enable logging by uncommenting the logging line +# log tracking measurements statistics + +# allow local connections to ntp +allow 10.0.0.0/24 + +# manual +manual + +# tell clients im the big dog +local stratum 1 + +# Good Time Servers +server time.cloudflare.com iburst diff --git a/working-normal-level-conf/gpsd b/working-normal-level-conf/gpsd new file mode 100644 index 0000000..afd5b95 --- /dev/null +++ b/working-normal-level-conf/gpsd @@ -0,0 +1,14 @@ +# USB might be /dev/ttyACM0 +# serial might be /dev/ttyS0 +# pps might be /dev/pps0 +DEVICES="/dev/ttyS0 /dev/pps0" + +# -n means start without a client connection (i.e. at boot) +# -s 115200 is to set baud to 115200 +GPSD_OPTIONS="-n -s 115200" + +# also start in general +START_DAEMON="true" + +# Automatically hot add/remove USB GPS devices via gpsdctl +USBAUTO="true" diff --git a/working-normal-level-conf/grafana.ini b/working-normal-level-conf/grafana.ini new file mode 100644 index 0000000..75ddff0 --- /dev/null +++ b/working-normal-level-conf/grafana.ini @@ -0,0 +1,26 @@ +instance_name = "Precision Timekeeping" + +[server] +# Protocol (http, https, h2, socket) +protocol = http + +# The http port to use +http_port = 3000 + +[log] +# Either "debug", "info", "warn", "error", "critical", default is "info" +level = info + +# Either "console", "file", "syslog". Default is console and file +# Use space to separate multiple modes, e.g. "console file" +mode = console file + +[log.file] +# log line format, valid options are text, console and json +format = text + +# This enables automated log rotate(switch of following options), default is true +log_rotate = true + +# Max line number of single file, default is 1000000 +max_lines = 1000000 \ No newline at end of file diff --git a/working-normal-level-conf/influxdb.conf b/working-normal-level-conf/influxdb.conf new file mode 100644 index 0000000..0d6a980 --- /dev/null +++ b/working-normal-level-conf/influxdb.conf @@ -0,0 +1,22 @@ +# report usage statistics to influxdb company +reporting-enabled = false + +[meta] + # Where the metadata/raft database is stored + dir = "/var/lib/influxdb/meta" + +[data] + # The directory where the TSM storage engine stores TSM files. + dir = "/var/lib/influxdb/data" + + # The directory where the TSM storage engine stores WAL files. + wal-dir = "/var/lib/influxdb/wal" + + # Trace logging provides more verbose output around the tsm engine. Turning + # this on can provide more useful output for debugging tsm engine issues. + trace-logging-enabled = false + +[logging] + format = "auto" + + level = "info" \ No newline at end of file diff --git a/working-normal-level-conf/telegraf.conf b/working-normal-level-conf/telegraf.conf new file mode 100644 index 0000000..5d2a382 --- /dev/null +++ b/working-normal-level-conf/telegraf.conf @@ -0,0 +1,22 @@ +[global_tags] +[agent] + interval = "10s" + round_interval = true + metric_batch_size = 10000 + metric_buffer_limit = 1000000 + collection_jitter = "0s" + flush_interval = "10s" + flush_jitter = "0s" + precision = "0s" + logfile = "/var/log/telegraf/telegraf.log" + # debug = true + +[[inputs.chrony]] + metrics = ["tracking"] + timeout = "5s" + +[[outputs.influxdb]] + urls = ["http://127.0.0.1:8086"] + database = "Chrony_Stats" + timeout = "5s" + # skip_database_creation = true diff --git a/working-warn-level-conf/50-tty.rules b/working-warn-level-conf/50-tty.rules new file mode 100644 index 0000000..9e6965d --- /dev/null +++ b/working-warn-level-conf/50-tty.rules @@ -0,0 +1 @@ +SUBSYSTEM=="pps*", GROUP="dialout", MODE="0660" \ No newline at end of file diff --git a/working-warn-level-conf/chrony.conf b/working-warn-level-conf/chrony.conf new file mode 100644 index 0000000..3028544 --- /dev/null +++ b/working-warn-level-conf/chrony.conf @@ -0,0 +1,31 @@ +# SHM refclock is shared memory driver, it is populated by GPSd and read by chrony +# it is SHM 0 +# refid is what we want to call this source = NMEA +# offset = 0.000 means we do not yet know the delay +# precision is how precise this is. not 1e-3 = 1 millisecond, so not very precision +# poll 0 means poll every 2^0 seconds = 1 second poll interval +# filter 3 means take the average/median (forget which) of the 3 most recent readings. NMEA can be jumpy so we're averaging here +refclock SHM 0 refid NMEA offset 0.000 precision 1e-3 poll 0 filter 3 + +# PPS refclock is PPS specific, with /dev/pps0 being the source +# refid PPS means call it the PPS source +# lock NMEA means this PPS source will also lock to the NMEA source for time of day info +# offset = 0.0 means no offset... this should probably always remain 0 +# poll 3 = poll every 2^3=8 seconds. polling more frequently isn't necessarily better +# trust means we trust this time. the NMEA will be kicked out as false ticker eventually, so we need to trust the combo +refclock PPS /dev/pps0 refid PPS lock NMEA offset 0.0 poll 3 trust + +# also enable logging by uncommenting the logging line +# log tracking measurements statistics + +# allow local connections to ntp +allow 10.0.0.0/24 + +# manual +manual + +# tell clients im the big dog +local stratum 1 + +# Good Time Servers +server time.cloudflare.com iburst diff --git a/working-warn-level-conf/gpsd b/working-warn-level-conf/gpsd new file mode 100644 index 0000000..afd5b95 --- /dev/null +++ b/working-warn-level-conf/gpsd @@ -0,0 +1,14 @@ +# USB might be /dev/ttyACM0 +# serial might be /dev/ttyS0 +# pps might be /dev/pps0 +DEVICES="/dev/ttyS0 /dev/pps0" + +# -n means start without a client connection (i.e. at boot) +# -s 115200 is to set baud to 115200 +GPSD_OPTIONS="-n -s 115200" + +# also start in general +START_DAEMON="true" + +# Automatically hot add/remove USB GPS devices via gpsdctl +USBAUTO="true" diff --git a/working-warn-level-conf/grafana.ini b/working-warn-level-conf/grafana.ini new file mode 100644 index 0000000..c5c4ef9 --- /dev/null +++ b/working-warn-level-conf/grafana.ini @@ -0,0 +1,26 @@ +instance_name = "Precision Timekeeping" + +[server] +# Protocol (http, https, h2, socket) +protocol = http + +# The http port to use +http_port = 3000 + +[log] +# Either "debug", "info", "warn", "error", "critical", default is "info" +level = warn + +# Either "console", "file", "syslog". Default is console and file +# Use space to separate multiple modes, e.g. "console file" +mode = console file + +[log.file] +# log line format, valid options are text, console and json +format = text + +# This enables automated log rotate(switch of following options), default is true +log_rotate = true + +# Max line number of single file, default is 1000000 +max_lines = 1000000 \ No newline at end of file diff --git a/working-warn-level-conf/influxdb.conf b/working-warn-level-conf/influxdb.conf new file mode 100644 index 0000000..00cca6d --- /dev/null +++ b/working-warn-level-conf/influxdb.conf @@ -0,0 +1,22 @@ +# report usage statistics to influxdb company +reporting-enabled = false + +[meta] + # Where the metadata/raft database is stored + dir = "/var/lib/influxdb/meta" + +[data] + # The directory where the TSM storage engine stores TSM files. + dir = "/var/lib/influxdb/data" + + # The directory where the TSM storage engine stores WAL files. + wal-dir = "/var/lib/influxdb/wal" + + # Trace logging provides more verbose output around the tsm engine. Turning + # this on can provide more useful output for debugging tsm engine issues. + trace-logging-enabled = false + +[logging] + format = "auto" + + level = "warn" \ No newline at end of file diff --git a/working-warn-level-conf/telegraf.conf b/working-warn-level-conf/telegraf.conf new file mode 100644 index 0000000..5d2a382 --- /dev/null +++ b/working-warn-level-conf/telegraf.conf @@ -0,0 +1,22 @@ +[global_tags] +[agent] + interval = "10s" + round_interval = true + metric_batch_size = 10000 + metric_buffer_limit = 1000000 + collection_jitter = "0s" + flush_interval = "10s" + flush_jitter = "0s" + precision = "0s" + logfile = "/var/log/telegraf/telegraf.log" + # debug = true + +[[inputs.chrony]] + metrics = ["tracking"] + timeout = "5s" + +[[outputs.influxdb]] + urls = ["http://127.0.0.1:8086"] + database = "Chrony_Stats" + timeout = "5s" + # skip_database_creation = true