{
  "apiVersion": "dashboard.grafana.app/v2",
  "kind": "Dashboard",
  "metadata": {
    "name": "lora-aprs-igate",
    "namespace": "default"
  },
  "spec": {
    "annotations": [
      {
        "kind": "AnnotationQuery",
        "spec": {
          "builtIn": true,
          "enable": true,
          "hide": true,
          "iconColor": "rgba(0, 211, 255, 1)",
          "name": "Annotations & Alerts",
          "query": {
            "datasource": {
              "name": "-- Grafana --"
            },
            "group": "grafana",
            "kind": "DataQuery",
            "spec": {},
            "version": "v0"
          }
        }
      }
    ],
    "cursorSync": "Off",
    "editable": true,
    "elements": {
      "panel-1": {
        "kind": "Panel",
        "spec": {
          "data": {
            "kind": "QueryGroup",
            "spec": {
              "queries": [
                {
                  "kind": "PanelQuery",
                  "spec": {
                    "hidden": false,
                    "query": {
                      "datasource": {
                        "name": "aprs_influxdb"
                      },
                      "group": "influxdb",
                      "kind": "DataQuery",
                      "spec": {
                        "query": "from(bucket: \"${bucket}\")\n  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n  |> filter(fn: (r) => r[\"_measurement\"] == \"aprs_syslog\")\n  |> filter(fn: (r) => r[\"category\"] =~ /^(${category_filter:regex})$/)\n  |> filter(fn: (r) => r[\"callsign\"] =~ /^(${station_filter:regex})$/)\n  |> filter(fn: (r) => r[\"callsign\"] != \"unknown\")\n  // count packets = distinct timestamps; tag each unique time with 1 and sum (counting\n  // _time directly errors: count cannot aggregate a time column)\n  |> keep(columns: [\"_time\", \"callsign\"])\n  |> group(columns: [\"callsign\"])\n  |> unique(column: \"_time\")\n  |> map(fn: (r) => ({callsign: r.callsign, _value: 1}))\n  |> sum()\n  |> group()\n  |> keep(columns: [\"callsign\", \"_value\"])\n  |> sort(columns: [\"_value\"], desc: true)"
                      },
                      "version": "v0"
                    },
                    "refId": "A"
                  }
                }
              ],
              "queryOptions": {},
              "transformations": [
                {
                  "group": "labelsToFields",
                  "kind": "Transformation",
                  "spec": {
                    "options": {
                      "keepLabels": [
                        "callsign"
                      ]
                    }
                  }
                }
              ]
            }
          },
          "description": "Who's been hogging the airwaves. Longer bar = bigger gob. No judgement. (Some judgement.)",
          "id": 1,
          "links": [],
          "title": "Station Activity",
          "vizConfig": {
            "group": "bargauge",
            "kind": "VizConfig",
            "spec": {
              "fieldConfig": {
                "defaults": {
                  "thresholds": {
                    "mode": "absolute",
                    "steps": [
                      {
                        "color": "green",
                        "value": 0
                      },
                      {
                        "color": "red",
                        "value": 80
                      }
                    ]
                  }
                },
                "overrides": []
              },
              "options": {
                "displayMode": "gradient",
                "legend": {
                  "calcs": [],
                  "displayMode": "list",
                  "placement": "bottom",
                  "showLegend": false
                },
                "maxVizHeight": 300,
                "minVizHeight": 16,
                "minVizWidth": 8,
                "namePlacement": "auto",
                "orientation": "vertical",
                "reduceOptions": {
                  "calcs": [
                    "lastNotNull"
                  ],
                  "fields": "",
                  "values": true
                },
                "showUnfilled": true,
                "sizing": "auto",
                "valueMode": "color"
              }
            },
            "version": "13.0.1+security-01"
          }
        }
      },
      "panel-2": {
        "kind": "Panel",
        "spec": {
          "data": {
            "kind": "QueryGroup",
            "spec": {
              "queries": [
                {
                  "kind": "PanelQuery",
                  "spec": {
                    "hidden": false,
                    "query": {
                      "datasource": {
                        "name": "aprs_influxdb"
                      },
                      "group": "influxdb",
                      "kind": "DataQuery",
                      "spec": {
                        "query": "from(bucket: \"${bucket}\")\n  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n  |> filter(fn: (r) => r[\"_measurement\"] == \"aprs_syslog\")\n  |> filter(fn: (r) => r[\"category\"] == \"RX\")\n  |> filter(fn: (r) => r[\"callsign\"] =~ /^(${station_filter:regex})$/)\n  |> filter(fn: (r) => r[\"callsign\"] != \"${bucket}\")\n  |> filter(fn: (r) => r[\"_field\"] =~ /^(${link_quality_fields:regex})$/)\n  |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false)\n  |> yield(name: \"last\")"
                      },
                      "version": "v0"
                    },
                    "refId": "A"
                  }
                }
              ],
              "queryOptions": {},
              "transformations": []
            }
          },
          "description": "How well the radios hear each other \u2014 higher SNR good, less-negative RSSI good, and they sit on separate axes on purpose so don't panic. \u26a0\ufe0f Heads up: the firmware force-feeds default values in some cases and there's nothing I can do about it. Sorry, not sorry.",
          "id": 2,
          "links": [],
          "title": "Link Quality",
          "vizConfig": {
            "group": "timeseries",
            "kind": "VizConfig",
            "spec": {
              "fieldConfig": {
                "defaults": {
                  "color": {
                    "mode": "palette-classic"
                  },
                  "custom": {
                    "axisBorderShow": false,
                    "axisCenteredZero": false,
                    "axisColorMode": "text",
                    "axisLabel": "",
                    "axisPlacement": "left",
                    "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": "always",
                    "showValues": false,
                    "spanNulls": false,
                    "stacking": {
                      "group": "A",
                      "mode": "none"
                    },
                    "thresholdsStyle": {
                      "mode": "off"
                    }
                  },
                  "fieldMinMax": false,
                  "thresholds": {
                    "mode": "absolute",
                    "steps": [
                      {
                        "color": "green",
                        "value": 0
                      },
                      {
                        "color": "red",
                        "value": 80
                      }
                    ]
                  }
                },
                "overrides": [
                  {
                    "matcher": {
                      "id": "byName",
                      "options": "snr",
                      "scope": "series"
                    },
                    "properties": [
                      {
                        "id": "custom.axisPlacement",
                        "value": "right"
                      }
                    ]
                  },
                  {
                    "matcher": {
                      "id": "byRegexp",
                      "options": ".*",
                      "scope": "series"
                    },
                    "properties": [
                      {
                        "id": "displayName",
                        "value": "${__field.labels.callsign} ${__field.name}"
                      }
                    ]
                  },
                  {
                    "matcher": {
                      "id": "byRegexp",
                      "options": "/^N1ABC-10 /"
                    },
                    "properties": [
                      {
                        "id": "color",
                        "value": {
                          "fixedColor": "orange",
                          "mode": "fixed"
                        }
                      }
                    ]
                  },
                  {
                    "matcher": {
                      "id": "byName",
                      "options": "N2DEF-10 rssi"
                    },
                    "properties": [
                      {
                        "id": "color",
                        "value": {
                          "fixedColor": "#5794F2",
                          "mode": "fixed"
                        }
                      }
                    ]
                  }
                ]
              },
              "options": {
                "annotations": {
                  "clustering": -1,
                  "multiLane": false
                },
                "legend": {
                  "calcs": [],
                  "displayMode": "list",
                  "placement": "right",
                  "showLegend": true
                },
                "tooltip": {
                  "hideZeros": false,
                  "mode": "single",
                  "sort": "none"
                }
              }
            },
            "version": "13.0.1+security-01"
          }
        }
      },
      "panel-4": {
        "kind": "Panel",
        "spec": {
          "data": {
            "kind": "QueryGroup",
            "spec": {
              "queries": [
                {
                  "kind": "PanelQuery",
                  "spec": {
                    "hidden": false,
                    "query": {
                      "datasource": {
                        "name": "aprs_influxdb"
                      },
                      "group": "influxdb",
                      "kind": "DataQuery",
                      "spec": {
                        "query": "toDeg = (v) => {\n    s = if v < 0.0 then -1.0 else 1.0\n    a = v * s\n    d = float(v: int(v: a / 100.0))\n    return s * (d + (a - d * 100.0) / 60.0)\n}\n// APRS coords are mixed: some rows are decimal degrees, some raw DDMM.mm. Decimal\n// latitude can never exceed 90, so |lat|>90 flags a DDMM row (lat AND lon) to convert.\nfixLat = (lat) => if (if lat < 0.0 then lat * -1.0 else lat) > 90.0 then toDeg(v: lat) else lat\nfixLon = (lat, lon) => if (if lat < 0.0 then lat * -1.0 else lat) > 90.0 then toDeg(v: lon) else lon\n\nfrom(bucket: \"${bucket}\")\n  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n  |> filter(fn: (r) => r[\"_measurement\"] == \"aprs_syslog\")\n  |> filter(fn: (r) => r[\"_field\"] == \"lat\" or r[\"_field\"] == \"lon\" or r[\"_field\"] == \"snr\")\n  |> group(columns: [\"callsign\", \"category\", \"_field\"])\n  |> last()\n  |> group()\n  // Pivot lat/lon/snr into columns. rowKey is callsign+category (NOT _time and NOT\n  // _field) so the three separately-timestamped fields collapse into one marker row.\n  |> pivot(rowKey:[\"callsign\", \"category\"], columnKey: [\"_field\"], valueColumn: \"_value\")\n  |> filter(fn: (r) => r.callsign =~ /^(${station_filter:regex})$/)\n  |> filter(fn: (r) => r[\"callsign\"] != \"${bucket}\")\n  |> filter(fn: (r) => r.category =~ /^(${category_filter:regex})$/)\n  |> filter(fn: (r) => exists r.lat and exists r.lon and r.lat != 0.0 and r.lon != 0.0)\n  |> map(fn: (r) => ({r with lat: fixLat(lat: r.lat), lon: fixLon(lat: r.lat, lon: r.lon)}))"
                      },
                      "version": "v0"
                    },
                    "refId": "A"
                  }
                },
                {
                  "kind": "PanelQuery",
                  "spec": {
                    "hidden": false,
                    "query": {
                      "datasource": {
                        "name": "aprs_influxdb"
                      },
                      "group": "influxdb",
                      "kind": "DataQuery",
                      "spec": {
                        "query": "import \"math\"\ntoDeg = (v) => { s = if v < 0.0 then -1.0 else 1.0\n  a = v * s\n  d = float(v: int(v: a / 100.0))\n  return s * (d + (a - d * 100.0) / 60.0) }\nfixLat = (lat) => if (if lat < 0.0 then lat * -1.0 else lat) > 90.0 then toDeg(v: lat) else lat\nfixLon = (lat, lon) => if (if lat < 0.0 then lat * -1.0 else lat) > 90.0 then toDeg(v: lon) else lon\nfrom(bucket: \"${bucket}\")\n  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n  |> filter(fn: (r) => r[\"callsign\"] == \"${bucket}\")\n  |> filter(fn: (r) => r[\"_field\"] == \"lat\" or r[\"_field\"] == \"lon\")\n  |> filter(fn: (r) => r._value != 0.0)\n  |> group(columns: [\"_field\"]) |> last() |> keep(columns: [\"_field\",\"_value\"])\n  |> map(fn: (r) => ({_field: r._field, _value: r._value, _key: \"gw\"}))\n  |> group()\n  |> pivot(rowKey: [\"_key\"], columnKey: [\"_field\"], valueColumn: \"_value\")\n  |> map(fn: (r) => ({lat: fixLat(lat: r.lat), lon: fixLon(lat: r.lat, lon: r.lon)}))"
                      },
                      "version": "v0"
                    },
                    "refId": "B"
                  }
                }
              ],
              "queryOptions": {},
              "transformations": []
            }
          },
          "description": "Coloured dots on a map. Greener dot = happier radio. Groundbreaking cartography, truly.",
          "id": 4,
          "links": [],
          "title": "Latest Distance and RSSI Map",
          "transparent": true,
          "vizConfig": {
            "group": "geomap",
            "kind": "VizConfig",
            "spec": {
              "fieldConfig": {
                "defaults": {
                  "color": {
                    "mode": "thresholds"
                  },
                  "custom": {
                    "hideFrom": {
                      "legend": false,
                      "tooltip": false,
                      "viz": false
                    }
                  },
                  "thresholds": {
                    "mode": "absolute",
                    "steps": [
                      {
                        "color": "red",
                        "value": null
                      },
                      {
                        "color": "orange",
                        "value": -18
                      },
                      {
                        "color": "#EAB839",
                        "value": -12
                      },
                      {
                        "color": "green",
                        "value": -3
                      }
                    ]
                  }
                },
                "overrides": []
              },
              "options": {
                "basemap": {
                  "config": {},
                  "name": "Layer 0",
                  "noRepeat": false,
                  "type": "default"
                },
                "controls": {
                  "mouseWheelZoom": true,
                  "showAttribution": true,
                  "showDebug": false,
                  "showMeasure": true,
                  "showScale": false,
                  "showZoom": true
                },
                "layers": [
                  {
                    "config": {
                      "showLegend": true,
                      "style": {
                        "color": {
                          "field": "snr",
                          "fixed": "dark-green"
                        },
                        "opacity": 0.9,
                        "rotation": {
                          "fixed": 0,
                          "max": 360,
                          "min": -360,
                          "mode": "mod"
                        },
                        "size": {
                          "fixed": 5,
                          "max": 15,
                          "min": 2
                        },
                        "symbol": {
                          "fixed": "img/icons/marker/circle.svg",
                          "mode": "fixed"
                        },
                        "symbolAlign": {
                          "horizontal": "center",
                          "vertical": "center"
                        },
                        "text": {
                          "field": "callsign",
                          "fixed": "",
                          "mode": "field"
                        },
                        "textConfig": {
                          "fontSize": 12,
                          "offsetX": 0,
                          "offsetY": 15,
                          "textAlign": "center",
                          "textBaseline": "middle"
                        }
                      }
                    },
                    "filterData": {
                      "id": "byRefId",
                      "options": "A"
                    },
                    "layer-tooltip": true,
                    "location": {
                      "latitude": "lat",
                      "longitude": "lon",
                      "mode": "coords"
                    },
                    "name": "Stations",
                    "tooltip": true,
                    "type": "markers"
                  },
                  {
                    "config": {
                      "showLegend": true,
                      "style": {
                        "color": {
                          "fixed": "red"
                        },
                        "opacity": 1,
                        "rotation": {
                          "fixed": 0,
                          "max": 360,
                          "min": -360,
                          "mode": "mod"
                        },
                        "size": {
                          "fixed": 10,
                          "max": 15,
                          "min": 2
                        },
                        "symbol": {
                          "fixed": "img/icons/marker/square.svg",
                          "mode": "fixed"
                        },
                        "symbolAlign": {
                          "horizontal": "center",
                          "vertical": "center"
                        },
                        "text": {
                          "fixed": "GATEWAY",
                          "mode": "fixed"
                        },
                        "textConfig": {
                          "fontSize": 14,
                          "fontWeight": "bold",
                          "offsetX": 0,
                          "offsetY": 20,
                          "textAlign": "center",
                          "textBaseline": "middle"
                        }
                      }
                    },
                    "filterData": {
                      "id": "byRefId",
                      "options": "B"
                    },
                    "layer-tooltip": true,
                    "location": {
                      "latitude": "lat",
                      "longitude": "lon",
                      "mode": "coords"
                    },
                    "name": "Gateway",
                    "tooltip": true,
                    "type": "markers"
                  }
                ],
                "tooltip": {
                  "mode": "details"
                },
                "view": {
                  "allLayers": true,
                  "dashboardVariable": false,
                  "id": "coords",
                  "lat": 53.704424,
                  "lon": -1.942539,
                  "noRepeat": false,
                  "shared": true,
                  "zoom": 11
                }
              }
            },
            "version": "13.0.1+security-01"
          }
        }
      },
      "panel-5": {
        "kind": "Panel",
        "spec": {
          "data": {
            "kind": "QueryGroup",
            "spec": {
              "queries": [
                {
                  "kind": "PanelQuery",
                  "spec": {
                    "hidden": false,
                    "query": {
                      "datasource": {
                        "name": "aprs_influxdb"
                      },
                      "group": "influxdb",
                      "kind": "DataQuery",
                      "spec": {
                        "query": "from(bucket: \"${bucket}\")\n  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n  |> filter(fn: (r) => r[\"_measurement\"] == \"aprs_syslog\")\n  |> filter(fn: (r) => r[\"_field\"] == \"battery\")\n  // Drop records that are 0 or null\n  |> filter(fn: (r) => exists r._value and r._value > 0)\n  |> filter(fn: (r) => r.callsign =~ /^(${station_filter:regex})$/)\n  |> filter(fn: (r) => r[\"callsign\"] != \"${bucket}\")\n  |> filter(fn: (r) => r.category =~ /^(${category_filter:regex})$/)\n  |> group(columns: [\"callsign\"])\n  |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n  |> yield(name: \"mean\")"
                      },
                      "version": "v0"
                    },
                    "refId": "A"
                  }
                }
              ],
              "queryOptions": {},
              "transformations": []
            }
          },
          "description": "Battery voltage over time. It goes down. Relatable.",
          "id": 5,
          "links": [],
          "title": "Battery stats",
          "vizConfig": {
            "group": "timeseries",
            "kind": "VizConfig",
            "spec": {
              "fieldConfig": {
                "defaults": {
                  "color": {
                    "mode": "palette-classic-by-name",
                    "seriesBy": "last"
                  },
                  "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",
                    "lineStyle": {
                      "fill": "solid"
                    },
                    "lineWidth": 1,
                    "pointSize": 5,
                    "scaleDistribution": {
                      "type": "linear"
                    },
                    "showPoints": "auto",
                    "showValues": true,
                    "spanNulls": true,
                    "stacking": {
                      "group": "A",
                      "mode": "none"
                    },
                    "thresholdsStyle": {
                      "mode": "off"
                    }
                  },
                  "fieldMinMax": false,
                  "thresholds": {
                    "mode": "absolute",
                    "steps": [
                      {
                        "color": "red",
                        "value": 0
                      },
                      {
                        "color": "yellow",
                        "value": 4
                      },
                      {
                        "color": "green",
                        "value": 4.2
                      }
                    ]
                  }
                },
                "overrides": []
              },
              "options": {
                "annotations": {
                  "clustering": -1,
                  "multiLane": false
                },
                "legend": {
                  "calcs": [
                    "last"
                  ],
                  "displayMode": "list",
                  "placement": "right",
                  "showLegend": true
                },
                "tooltip": {
                  "hideZeros": false,
                  "mode": "single",
                  "sort": "none"
                }
              }
            },
            "version": "13.0.1+security-01"
          }
        }
      },
      "panel-6": {
        "kind": "Panel",
        "spec": {
          "data": {
            "kind": "QueryGroup",
            "spec": {
              "queries": [
                {
                  "kind": "PanelQuery",
                  "spec": {
                    "hidden": false,
                    "query": {
                      "datasource": {
                        "name": "aprs_influxdb"
                      },
                      "group": "influxdb",
                      "kind": "DataQuery",
                      "spec": {
                        "query": "import \"math\"\ntoDeg = (v) => { s = if v < 0.0 then -1.0 else 1.0\n  a = v * s\n  d = float(v: int(v: a / 100.0))\n  return s * (d + (a - d * 100.0) / 60.0) }\nfixLat = (lat) => if (if lat < 0.0 then lat * -1.0 else lat) > 90.0 then toDeg(v: lat) else lat\nfixLon = (lat, lon) => if (if lat < 0.0 then lat * -1.0 else lat) > 90.0 then toDeg(v: lon) else lon\nfrom(bucket: \"${bucket}\")\n  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n  |> filter(fn: (r) => r[\"_measurement\"] == \"aprs_syslog\")\n  |> filter(fn: (r) => r[\"category\"] == \"RX\")\n  |> filter(fn: (r) => r[\"_field\"] == \"lat\" or r[\"_field\"] == \"lon\")\n  |> filter(fn: (r) => r.callsign =~ /^(${station_filter:regex})$/)\n  |> filter(fn: (r) => r[\"callsign\"] != \"${bucket}\" and r[\"callsign\"] != \"unknown\")\n  |> filter(fn: (r) => r._value != 0.0)\n  |> pivot(rowKey: [\"_time\",\"callsign\"], columnKey: [\"_field\"], valueColumn: \"_value\")\n  |> filter(fn: (r) => exists r.lat and exists r.lon and r.lat != 0.0 and r.lon != 0.0)\n  |> map(fn: (r) => {\n      ws = float(v: uint(v: v.timeRangeStart))\n      we = float(v: uint(v: v.timeRangeStop))\n      span = if we > ws then we - ws else 1.0\n      return {_time: r._time, callsign: r.callsign,\n        lat: fixLat(lat: r.lat), lon: fixLon(lat: r.lat, lon: r.lon),\n        pct: (float(v: uint(v: r._time)) - ws) / span * 100.0}\n  })"
                      },
                      "version": "v0"
                    },
                    "refId": "A"
                  }
                },
                {
                  "kind": "PanelQuery",
                  "spec": {
                    "hidden": false,
                    "query": {
                      "datasource": {
                        "name": "aprs_influxdb"
                      },
                      "group": "influxdb",
                      "kind": "DataQuery",
                      "spec": {
                        "query": "import \"math\"\ntoDeg = (v) => { s = if v < 0.0 then -1.0 else 1.0\n  a = v * s\n  d = float(v: int(v: a / 100.0))\n  return s * (d + (a - d * 100.0) / 60.0) }\nfixLat = (lat) => if (if lat < 0.0 then lat * -1.0 else lat) > 90.0 then toDeg(v: lat) else lat\nfixLon = (lat, lon) => if (if lat < 0.0 then lat * -1.0 else lat) > 90.0 then toDeg(v: lon) else lon\nfrom(bucket: \"${bucket}\")\n  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n  |> filter(fn: (r) => r[\"_measurement\"] == \"aprs_syslog\")\n  |> filter(fn: (r) => r[\"category\"] == \"RX\")\n  |> filter(fn: (r) => r[\"_field\"] == \"lat\" or r[\"_field\"] == \"lon\")\n  |> filter(fn: (r) => r.callsign =~ /^(${station_filter:regex})$/)\n  |> filter(fn: (r) => r[\"callsign\"] != \"${bucket}\" and r[\"callsign\"] != \"unknown\")\n  |> filter(fn: (r) => r._value != 0.0)\n  |> pivot(rowKey: [\"_time\",\"callsign\"], columnKey: [\"_field\"], valueColumn: \"_value\")\n  |> filter(fn: (r) => exists r.lat and exists r.lon and r.lat != 0.0 and r.lon != 0.0)\n  |> group(columns: [\"callsign\"]) |> last(column: \"_time\") |> group()\n  |> map(fn: (r) => ({callsign: r.callsign, lat: fixLat(lat: r.lat), lon: fixLon(lat: r.lat, lon: r.lon)}))"
                      },
                      "version": "v0"
                    },
                    "refId": "B"
                  }
                },
                {
                  "kind": "PanelQuery",
                  "spec": {
                    "hidden": false,
                    "query": {
                      "datasource": {
                        "name": "aprs_influxdb"
                      },
                      "group": "influxdb",
                      "kind": "DataQuery",
                      "spec": {
                        "query": "import \"math\"\ntoDeg = (v) => { s = if v < 0.0 then -1.0 else 1.0\n  a = v * s\n  d = float(v: int(v: a / 100.0))\n  return s * (d + (a - d * 100.0) / 60.0) }\nfixLat = (lat) => if (if lat < 0.0 then lat * -1.0 else lat) > 90.0 then toDeg(v: lat) else lat\nfixLon = (lat, lon) => if (if lat < 0.0 then lat * -1.0 else lat) > 90.0 then toDeg(v: lon) else lon\nfrom(bucket: \"${bucket}\")\n  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n  |> filter(fn: (r) => r[\"callsign\"] == \"${bucket}\")\n  |> filter(fn: (r) => r[\"_field\"] == \"lat\" or r[\"_field\"] == \"lon\")\n  |> filter(fn: (r) => r._value != 0.0)\n  |> group(columns: [\"_field\"]) |> last() |> keep(columns: [\"_field\",\"_value\"])\n  |> map(fn: (r) => ({_field: r._field, _value: r._value, _key: \"gw\"}))\n  |> group()\n  |> pivot(rowKey: [\"_key\"], columnKey: [\"_field\"], valueColumn: \"_value\")\n  |> map(fn: (r) => ({lat: fixLat(lat: r.lat), lon: fixLon(lat: r.lat, lon: r.lon)}))"
                      },
                      "version": "v0"
                    },
                    "refId": "C"
                  }
                }
              ],
              "queryOptions": {},
              "transformations": []
            }
          },
          "description": "Each station's positions over the SELECTED time window. The dot trail is coloured by where it falls in that window (blue = oldest in view -> red = newest), so journeys read correctly whether you're viewing the last 3 days or a week from months ago. White labelled dots = each station's latest position in view.",
          "id": 6,
          "links": [],
          "title": "Location over Time",
          "vizConfig": {
            "group": "geomap",
            "kind": "VizConfig",
            "spec": {
              "fieldConfig": {
                "defaults": {
                  "color": {
                    "mode": "thresholds"
                  },
                  "thresholds": {
                    "mode": "absolute",
                    "steps": [
                      {
                        "color": "#5794F2",
                        "value": null
                      },
                      {
                        "color": "#73BF69",
                        "value": 20
                      },
                      {
                        "color": "#FADE2A",
                        "value": 40
                      },
                      {
                        "color": "#FF9830",
                        "value": 60
                      },
                      {
                        "color": "#F2495C",
                        "value": 80
                      }
                    ]
                  }
                },
                "overrides": []
              },
              "options": {
                "basemap": {
                  "config": {},
                  "name": "Layer 0",
                  "noRepeat": false,
                  "opacity": 1,
                  "type": "default"
                },
                "controls": {
                  "mouseWheelZoom": true,
                  "showAttribution": true,
                  "showDebug": false,
                  "showMeasure": false,
                  "showScale": false,
                  "showZoom": true
                },
                "layers": [
                  {
                    "config": {
                      "showLegend": true,
                      "style": {
                        "color": {
                          "field": "pct",
                          "fixed": "green"
                        },
                        "opacity": 0.6,
                        "rotation": {
                          "fixed": 0,
                          "max": 360,
                          "min": -360,
                          "mode": "mod"
                        },
                        "size": {
                          "fixed": 4,
                          "max": 15,
                          "min": 2
                        },
                        "symbol": {
                          "fixed": "img/icons/marker/circle.svg",
                          "mode": "fixed"
                        },
                        "symbolAlign": {
                          "horizontal": "center",
                          "vertical": "center"
                        },
                        "text": {
                          "fixed": "",
                          "mode": "fixed"
                        },
                        "textConfig": {
                          "fontSize": 11,
                          "offsetX": 0,
                          "offsetY": 14,
                          "textAlign": "center",
                          "textBaseline": "middle"
                        }
                      }
                    },
                    "filterData": {
                      "id": "byRefId",
                      "options": "A"
                    },
                    "location": {
                      "latitude": "lat",
                      "longitude": "lon",
                      "mode": "coords"
                    },
                    "name": "Trail (old \u2192 new)",
                    "tooltip": true,
                    "type": "markers"
                  },
                  {
                    "config": {
                      "showLegend": true,
                      "style": {
                        "color": {
                          "fixed": "#FFFFFF"
                        },
                        "opacity": 1.0,
                        "rotation": {
                          "fixed": 0,
                          "max": 360,
                          "min": -360,
                          "mode": "mod"
                        },
                        "size": {
                          "fixed": 8,
                          "max": 15,
                          "min": 2
                        },
                        "symbol": {
                          "fixed": "img/icons/marker/circle.svg",
                          "mode": "fixed"
                        },
                        "symbolAlign": {
                          "horizontal": "center",
                          "vertical": "center"
                        },
                        "text": {
                          "field": "callsign",
                          "mode": "field",
                          "fixed": ""
                        },
                        "textConfig": {
                          "fontSize": 11,
                          "offsetX": 0,
                          "offsetY": 14,
                          "textAlign": "center",
                          "textBaseline": "middle"
                        }
                      }
                    },
                    "filterData": {
                      "id": "byRefId",
                      "options": "B"
                    },
                    "location": {
                      "latitude": "lat",
                      "longitude": "lon",
                      "mode": "coords"
                    },
                    "name": "Current position",
                    "tooltip": true,
                    "type": "markers"
                  },
                  {
                    "config": {
                      "showLegend": true,
                      "style": {
                        "color": {
                          "fixed": "red"
                        },
                        "opacity": 1,
                        "rotation": {
                          "fixed": 0,
                          "max": 360,
                          "min": -360,
                          "mode": "mod"
                        },
                        "size": {
                          "fixed": 11,
                          "max": 15,
                          "min": 2
                        },
                        "symbol": {
                          "fixed": "img/icons/marker/square.svg",
                          "mode": "fixed"
                        },
                        "symbolAlign": {
                          "horizontal": "center",
                          "vertical": "center"
                        },
                        "text": {
                          "fixed": "GATEWAY",
                          "mode": "fixed"
                        },
                        "textConfig": {
                          "fontSize": 14,
                          "fontWeight": "bold",
                          "offsetX": 0,
                          "offsetY": 20,
                          "textAlign": "center",
                          "textBaseline": "middle"
                        }
                      }
                    },
                    "filterData": {
                      "id": "byRefId",
                      "options": "C"
                    },
                    "layer-tooltip": true,
                    "location": {
                      "latitude": "lat",
                      "longitude": "lon",
                      "mode": "coords"
                    },
                    "name": "Gateway",
                    "tooltip": true,
                    "type": "markers"
                  }
                ],
                "tooltip": {
                  "mode": "details"
                },
                "view": {
                  "allLayers": true,
                  "dashboardVariable": false,
                  "id": "coords",
                  "lat": 53.714047,
                  "lon": -1.900831,
                  "noRepeat": false,
                  "zoom": 12.53
                }
              }
            },
            "version": "13.0.1+security-01"
          }
        }
      },
      "panel-7": {
        "kind": "Panel",
        "spec": {
          "data": {
            "kind": "QueryGroup",
            "spec": {
              "queries": [
                {
                  "kind": "PanelQuery",
                  "spec": {
                    "hidden": false,
                    "query": {
                      "datasource": {
                        "name": "aprs_influxdb"
                      },
                      "group": "influxdb",
                      "kind": "DataQuery",
                      "spec": {
                        "query": "from(bucket: \"${bucket}\")\n  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n  |> filter(fn: (r) => r[\"_measurement\"] == \"aprs_syslog\")\n  |> filter(fn: (r) => r[\"category\"] =~ /^(${category_filter:regex})$/)\n  |> filter(fn: (r) => r[\"callsign\"] =~ /^(${station_filter:regex})$/)\n  // one row per packet (unique timestamp), tagged with a 1 to count, so we count\n  // packets not field-rows and never mix the string log field into the group\n  |> keep(columns: [\"_time\", \"callsign\", \"category\"])\n  |> group(columns: [\"callsign\", \"category\"])\n  |> unique(column: \"_time\")\n  |> map(fn: (r) => ({_time: r._time, callsign: r.callsign, category: r.category, _value: 1}))\n  |> group(columns: [\"callsign\", \"category\"])\n  |> aggregateWindow(every: v.windowPeriod, fn: count, createEmpty: false)\n  |> yield(name: \"count\")"
                      },
                      "version": "v0"
                    },
                    "refId": "A"
                  }
                }
              ],
              "queryOptions": {},
              "transformations": []
            }
          },
          "description": "Packets per station per window. Line go up, line go down, line owes you nothing and frankly neither do I at this point.",
          "id": 7,
          "links": [],
          "title": "Packet Rate per Station",
          "vizConfig": {
            "group": "timeseries",
            "kind": "VizConfig",
            "spec": {
              "fieldConfig": {
                "defaults": {
                  "color": {
                    "mode": "palette-classic"
                  },
                  "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",
                    "lineStyle": {
                      "fill": "solid"
                    },
                    "lineWidth": 1,
                    "pointSize": 5,
                    "scaleDistribution": {
                      "type": "linear"
                    },
                    "showPoints": "auto",
                    "showValues": false,
                    "spanNulls": true,
                    "stacking": {
                      "group": "A",
                      "mode": "none"
                    },
                    "thresholdsStyle": {
                      "mode": "off"
                    }
                  },
                  "fieldMinMax": false
                },
                "overrides": []
              },
              "options": {
                "annotations": {
                  "clustering": -1,
                  "multiLane": false
                },
                "legend": {
                  "calcs": [
                    "last"
                  ],
                  "displayMode": "list",
                  "placement": "right",
                  "showLegend": true
                },
                "tooltip": {
                  "hideZeros": false,
                  "mode": "single",
                  "sort": "none"
                }
              }
            },
            "version": "13.0.1+security-01"
          }
        }
      },
      "panel-8": {
        "kind": "Panel",
        "spec": {
          "data": {
            "kind": "QueryGroup",
            "spec": {
              "queries": [
                {
                  "kind": "PanelQuery",
                  "spec": {
                    "hidden": false,
                    "query": {
                      "datasource": {
                        "name": "aprs_influxdb"
                      },
                      "group": "influxdb",
                      "kind": "DataQuery",
                      "spec": {
                        "query": "from(bucket: \"${bucket}\")\n  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n  |> filter(fn: (r) => r[\"_measurement\"] == \"aprs_syslog\")\n  |> filter(fn: (r) => r[\"category\"] == \"RX\")\n  |> filter(fn: (r) => r[\"callsign\"] =~ /^(${station_filter:regex})$/)\n  |> filter(fn: (r) => r[\"callsign\"] != \"${bucket}\" and r[\"callsign\"] != \"unknown\")\n  |> filter(fn: (r) => r[\"_field\"] == \"rssi\" or r[\"_field\"] == \"snr\")\n  |> group(columns: [\"callsign\", \"_field\"])\n  |> mean()\n  |> group()\n  // pivot in Flux (more reliable than the table transform that returned nothing)\n  |> pivot(rowKey: [\"callsign\"], columnKey: [\"_field\"], valueColumn: \"_value\")\n  |> sort(columns: [\"rssi\"])\n  |> yield(name: \"mean_link_quality\")"
                      },
                      "version": "v0"
                    },
                    "refId": "A"
                  }
                }
              ],
              "queryOptions": {},
              "transformations": []
            }
          },
          "description": "Average RSSI and SNR per station, in a nice little grid. We have officially become a spreadsheet. This is where dashboards come to retire.",
          "id": 8,
          "links": [],
          "title": "Average Link Quality",
          "vizConfig": {
            "group": "bargauge",
            "kind": "VizConfig",
            "spec": {
              "fieldConfig": {
                "defaults": {
                  "color": {
                    "mode": "thresholds"
                  },
                  "mappings": [],
                  "thresholds": {
                    "mode": "absolute",
                    "steps": [
                      {
                        "color": "green",
                        "value": null
                      }
                    ]
                  }
                },
                "overrides": [
                  {
                    "matcher": {
                      "id": "byName",
                      "options": "rssi"
                    },
                    "properties": [
                      {
                        "id": "min",
                        "value": -120
                      },
                      {
                        "id": "max",
                        "value": -80
                      },
                      {
                        "id": "decimals",
                        "value": 0
                      },
                      {
                        "id": "unit",
                        "value": "dBm"
                      },
                      {
                        "id": "thresholds",
                        "value": {
                          "mode": "absolute",
                          "steps": [
                            {
                              "color": "red",
                              "value": null
                            },
                            {
                              "color": "orange",
                              "value": -108
                            },
                            {
                              "color": "#EAB839",
                              "value": -100
                            },
                            {
                              "color": "green",
                              "value": -92
                            }
                          ]
                        }
                      },
                      {
                        "id": "color",
                        "value": {
                          "mode": "thresholds"
                        }
                      }
                    ]
                  },
                  {
                    "matcher": {
                      "id": "byName",
                      "options": "snr"
                    },
                    "properties": [
                      {
                        "id": "min",
                        "value": -20
                      },
                      {
                        "id": "max",
                        "value": 12
                      },
                      {
                        "id": "decimals",
                        "value": 1
                      },
                      {
                        "id": "unit",
                        "value": "dB"
                      },
                      {
                        "id": "thresholds",
                        "value": {
                          "mode": "absolute",
                          "steps": [
                            {
                              "color": "red",
                              "value": null
                            },
                            {
                              "color": "orange",
                              "value": -18
                            },
                            {
                              "color": "#EAB839",
                              "value": -12
                            },
                            {
                              "color": "green",
                              "value": -3
                            }
                          ]
                        }
                      },
                      {
                        "id": "color",
                        "value": {
                          "mode": "thresholds"
                        }
                      }
                    ]
                  }
                ]
              },
              "options": {
                "displayMode": "gradient",
                "orientation": "horizontal",
                "showUnfilled": true,
                "valueMode": "color",
                "namePlacement": "left",
                "sizing": "auto",
                "minVizHeight": 12,
                "minVizWidth": 0,
                "maxVizHeight": 300,
                "reduceOptions": {
                  "values": true,
                  "calcs": [
                    "lastNotNull"
                  ],
                  "fields": ""
                }
              }
            },
            "version": "13.0.1+security-01"
          }
        }
      },
      "panel-9": {
        "kind": "Panel",
        "spec": {
          "data": {
            "kind": "QueryGroup",
            "spec": {
              "queries": [
                {
                  "kind": "PanelQuery",
                  "spec": {
                    "hidden": false,
                    "query": {
                      "datasource": {
                        "name": "aprs_influxdb"
                      },
                      "group": "influxdb",
                      "kind": "DataQuery",
                      "spec": {
                        "query": "from(bucket: \"${bucket}\")\n  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n  |> filter(fn: (r) => r[\"_measurement\"] == \"aprs_syslog\")\n  |> filter(fn: (r) => r[\"callsign\"] =~ /^(${station_filter:regex})$/)\n  // packets per category = distinct timestamps; tag each unique time with 1 and sum\n  |> keep(columns: [\"_time\", \"category\"])\n  |> group(columns: [\"category\"])\n  |> unique(column: \"_time\")\n  |> map(fn: (r) => ({category: r.category, _value: 1}))\n  |> sum()\n  |> group()\n  |> keep(columns: [\"category\", \"_value\"])"
                      },
                      "version": "v0"
                    },
                    "refId": "A"
                  }
                }
              ],
              "queryOptions": {},
              "transformations": []
            }
          },
          "description": "A pie chart of packet types (RX / TX / TCPIP / SYSTEM / CRC_ERROR). It's pie. You scrolled all the way down here for pie. There is no prize. Go outside. Touch grass. I'll still be here.",
          "id": 9,
          "links": [],
          "title": "Packet Category Distribution",
          "vizConfig": {
            "group": "piechart",
            "kind": "VizConfig",
            "spec": {
              "fieldConfig": {
                "defaults": {
                  "color": {
                    "mode": "palette-classic"
                  },
                  "custom": {
                    "hideFrom": {
                      "legend": false,
                      "tooltip": false,
                      "viz": false
                    }
                  }
                },
                "overrides": []
              },
              "options": {
                "displayLabels": [
                  "name",
                  "percent"
                ],
                "pieType": "pie",
                "reduceOptions": {
                  "calcs": [
                    "lastNotNull"
                  ],
                  "fields": "",
                  "values": true
                },
                "tooltip": {
                  "mode": "single"
                }
              }
            },
            "version": "13.0.1+security-01"
          }
        }
      },
      "panel-10": {
        "kind": "Panel",
        "spec": {
          "data": {
            "kind": "QueryGroup",
            "spec": {
              "queries": [
                {
                  "kind": "PanelQuery",
                  "spec": {
                    "hidden": false,
                    "query": {
                      "datasource": {
                        "name": "aprs_influxdb"
                      },
                      "group": "influxdb",
                      "kind": "DataQuery",
                      "spec": {
                        "query": "from(bucket: \"${bucket}\")\n  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n  |> filter(fn: (r) => r[\"_measurement\"] == \"aprs_syslog\")\n  |> filter(fn: (r) => r[\"callsign\"] =~ /^(${station_filter:regex})$/)\n  |> filter(fn: (r) => r[\"category\"] == \"RX\" or r[\"category\"] == \"CRC_ERROR\")\n  // count packets (unique timestamps) per category, independent of how many fields\n  // each packet wrote, so the good-vs-bad ratio is honest\n  |> keep(columns: [\"_time\", \"category\"])\n  |> group(columns: [\"category\"])\n  |> unique(column: \"_time\")\n  |> map(fn: (r) => ({_time: r._time, category: r.category, _value: 1}))\n  |> group(columns: [\"category\"])\n  |> aggregateWindow(every: v.windowPeriod, fn: count, createEmpty: true)\n  |> fill(value: 0)\n  |> pivot(rowKey: [\"_time\"], columnKey: [\"category\"], valueColumn: \"_value\")\n  |> map(fn: (r) => {\n      crc = if exists r.CRC_ERROR then r.CRC_ERROR else 0\n      rx = if exists r.RX then r.RX else 0\n      total = crc + rx\n      return {_time: r._time, _value: if total > 0 then float(v: crc) / float(v: total) * 100.0 else 0.0}\n  })\n  |> yield(name: \"crc_rate\")"
                      },
                      "version": "v0"
                    },
                    "refId": "A"
                  }
                }
              ],
              "queryOptions": {},
              "transformations": []
            }
          },
          "description": "The percentage of packets that arrived as pure gibberish. Higher = your radio is basically lithobraking into the noise floor. Lower = good. You want low. Trust me on this one.",
          "id": 10,
          "links": [],
          "title": "CRC Error Rate",
          "vizConfig": {
            "group": "timeseries",
            "kind": "VizConfig",
            "spec": {
              "fieldConfig": {
                "defaults": {
                  "color": {
                    "mode": "thresholds"
                  },
                  "custom": {
                    "axisBorderShow": false,
                    "axisCenteredZero": false,
                    "axisColorMode": "text",
                    "axisLabel": "",
                    "axisPlacement": "auto",
                    "barAlignment": 0,
                    "drawStyle": "line",
                    "fillOpacity": 10,
                    "gradientMode": "none",
                    "hideFrom": {
                      "legend": false,
                      "tooltip": false,
                      "viz": false
                    },
                    "insertNulls": false,
                    "lineInterpolation": "linear",
                    "lineWidth": 1,
                    "pointSize": 5,
                    "scaleDistribution": {
                      "type": "linear"
                    },
                    "showPoints": "auto",
                    "spanNulls": true,
                    "stacking": {
                      "group": "A",
                      "mode": "none"
                    },
                    "thresholdsStyle": {
                      "mode": "dashed"
                    }
                  },
                  "decimals": 1,
                  "min": 0,
                  "unit": "percent",
                  "thresholds": {
                    "mode": "absolute",
                    "steps": [
                      {
                        "color": "green",
                        "value": 0
                      },
                      {
                        "color": "yellow",
                        "value": 5
                      },
                      {
                        "color": "red",
                        "value": 15
                      }
                    ]
                  }
                },
                "overrides": []
              },
              "options": {
                "legend": {
                  "calcs": [
                    "mean",
                    "max"
                  ],
                  "displayMode": "list",
                  "placement": "right",
                  "showLegend": true
                },
                "tooltip": {
                  "hideZeros": false,
                  "mode": "single",
                  "sort": "none"
                }
              }
            },
            "version": "13.0.1+security-01"
          }
        }
      },
      "panel-11": {
        "kind": "Panel",
        "spec": {
          "data": {
            "kind": "QueryGroup",
            "spec": {
              "queries": [
                {
                  "kind": "PanelQuery",
                  "spec": {
                    "hidden": false,
                    "query": {
                      "datasource": {
                        "name": "aprs_influxdb"
                      },
                      "group": "influxdb",
                      "kind": "DataQuery",
                      "spec": {
                        "query": "from(bucket: \"${bucket}\")\n  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n  |> filter(fn: (r) => r[\"_measurement\"] == \"aprs_syslog\")\n  |> filter(fn: (r) => r[\"callsign\"] =~ /^(${station_filter:regex})$/)\n  |> filter(fn: (r) => r[\"callsign\"] != \"unknown\")\n  |> filter(fn: (r) => r[\"callsign\"] != \"${bucket}\")\n  // numeric fields only -> avoids merging the string log field (which errors on group)\n  |> filter(fn: (r) => r[\"_field\"] == \"rssi\" or r[\"_field\"] == \"snr\" or r[\"_field\"] == \"lat\" or r[\"_field\"] == \"lon\" or r[\"_field\"] == \"battery\")\n  |> group(columns: [\"callsign\"])\n  |> last()\n  |> keep(columns: [\"callsign\", \"_time\"])\n  |> group()\n  |> map(fn: (r) => ({\n      callsign: r.callsign,\n      last_heard: r._time,\n      age_seconds: float(v: uint(v: now()) - uint(v: r._time)) / 1000000000.0\n  }))\n  |> sort(columns: [\"age_seconds\"], desc: true)"
                      },
                      "version": "v0"
                    },
                    "refId": "A"
                  }
                }
              ],
              "queryOptions": {},
              "transformations": []
            }
          },
          "description": "How long since each station last uttered a single packet. Small green number = chatty and alive. Big red number = they have ghosted you and aren't coming back. We've all been there.",
          "id": 11,
          "links": [],
          "title": "Last Heard",
          "vizConfig": {
            "group": "table",
            "kind": "VizConfig",
            "spec": {
              "fieldConfig": {
                "defaults": {
                  "color": {
                    "mode": "thresholds"
                  },
                  "custom": {
                    "align": "auto",
                    "cellOptions": {
                      "type": "auto"
                    },
                    "inspect": false
                  }
                },
                "overrides": [
                  {
                    "matcher": {
                      "id": "byName",
                      "options": "age_seconds"
                    },
                    "properties": [
                      {
                        "id": "unit",
                        "value": "s"
                      },
                      {
                        "id": "custom.cellOptions",
                        "value": {
                          "type": "color-background",
                          "mode": "gradient"
                        }
                      },
                      {
                        "id": "thresholds",
                        "value": {
                          "mode": "absolute",
                          "steps": [
                            {
                              "color": "green",
                              "value": 0
                            },
                            {
                              "color": "yellow",
                              "value": 900
                            },
                            {
                              "color": "red",
                              "value": 3600
                            }
                          ]
                        }
                      }
                    ]
                  }
                ]
              },
              "options": {
                "cellHeight": "sm",
                "footer": {
                  "show": false
                },
                "showHeader": true
              }
            },
            "version": "13.0.1+security-01"
          }
        }
      },
      "panel-12": {
        "kind": "Panel",
        "spec": {
          "data": {
            "kind": "QueryGroup",
            "spec": {
              "queries": [
                {
                  "kind": "PanelQuery",
                  "spec": {
                    "hidden": false,
                    "query": {
                      "datasource": {
                        "name": "aprs_influxdb"
                      },
                      "group": "influxdb",
                      "kind": "DataQuery",
                      "spec": {
                        "query": "import \"math\"\nimport \"array\"\ntoDeg = (v) => { s = if v < 0.0 then -1.0 else 1.0\n  a = v * s\n  d = float(v: int(v: a / 100.0))\n  return s * (d + (a - d * 100.0) / 60.0) }\nfixLat = (lat) => if (if lat < 0.0 then lat * -1.0 else lat) > 90.0 then toDeg(v: lat) else lat\nfixLon = (lat, lon) => if (if lat < 0.0 then lat * -1.0 else lat) > 90.0 then toDeg(v: lon) else lon\ngwBase = from(bucket: \"${bucket}\")\n  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n  |> filter(fn: (r) => r[\"callsign\"] == \"${bucket}\")\n  |> filter(fn: (r) => r[\"_field\"] == \"lat\" or r[\"_field\"] == \"lon\")\n  |> filter(fn: (r) => r._value != 0.0)\n  |> group(columns: [\"_field\"]) |> last() |> keep(columns: [\"_field\",\"_value\"]) |> group()\n  |> map(fn: (r) => ({_field: r._field, _value: r._value, _key: \"gw\"}))\n  |> pivot(rowKey: [\"_key\"], columnKey: [\"_field\"], valueColumn: \"_value\")\ngw = union(tables: [gwBase, array.from(rows: [{_key: \"~none\", lat: 0.0, lon: 0.0}])])\n  |> group() |> sort(columns: [\"_key\"]) |> findRecord(fn: (key) => true, idx: 0)\ngwLat = fixLat(lat: gw.lat)\ngwLon = fixLon(lat: gw.lat, lon: gw.lon)\n\nfrom(bucket: \"${bucket}\")\n  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n  |> filter(fn: (r) => r[\"category\"] == \"RX\")\n  |> filter(fn: (r) => r[\"callsign\"] =~ /^(${station_filter:regex})$/)\n  |> filter(fn: (r) => r[\"callsign\"] != \"${bucket}\" and r[\"callsign\"] != \"unknown\")\n  |> filter(fn: (r) => r[\"_field\"] == \"lat\" or r[\"_field\"] == \"lon\" or r[\"_field\"] == \"digipeated\")\n  |> pivot(rowKey: [\"_time\",\"callsign\"], columnKey: [\"_field\"], valueColumn: \"_value\")\n  |> filter(fn: (r) => exists r.lat and exists r.lon and r.lat != 0.0 and r.lon != 0.0)\n  |> map(fn: (r) => {\n      lat2 = fixLat(lat: r.lat)\n      lon2 = fixLon(lat: r.lat, lon: r.lon)\n      dLat = math.pi / 180.0 * (lat2 - gwLat)\n      dLon = math.pi / 180.0 * (lon2 - gwLon)\n      a = math.sin(x: dLat/2.0)*math.sin(x: dLat/2.0) + math.cos(x: math.pi/180.0*gwLat)*math.cos(x: math.pi/180.0*lat2)*math.sin(x: dLon/2.0)*math.sin(x: dLon/2.0)\n      c = 2.0 * math.atan2(y: math.sqrt(x: a), x: math.sqrt(x: 1.0 - a))\n      return {callsign: r.callsign, distance_m: 6371000.0 * c, dg: if exists r.digipeated then r.digipeated else -1.0}\n  })\n  |> filter(fn: (r) => r.distance_m < 2000000.0)\n  |> group(columns: [\"callsign\"])\n  |> reduce(fn: (r, accumulator) => ({\n        distance_m: if r.distance_m > accumulator.distance_m then r.distance_m else accumulator.distance_m,\n        ev_direct: if r.dg == 0.0 then 1.0 else accumulator.ev_direct,\n        ev_digi: if r.dg == 1.0 then 1.0 else accumulator.ev_digi\n     }), identity: {distance_m: 0.0, ev_direct: 0.0, ev_digi: 0.0})\n  |> group()\n  |> map(fn: (r) => ({callsign: r.callsign, distance_m: r.distance_m,\n        rf: if (r.ev_direct == 0.0 and r.ev_digi > 0.0) then \"via digi\" else \"direct\"}))\n  |> sort(columns: [\"distance_m\"], desc: true)"
                      },
                      "version": "v0"
                    },
                    "refId": "A"
                  }
                }
              ],
              "queryOptions": {},
              "transformations": []
            }
          },
          "description": "Longest-range stations. Crucially distinguishes DIRECT RF contacts (green) from packets only heard VIA A DIGIPEATER (orange) - a 'via digi' station may be geographically far but was NOT a direct radio contact, so the true DX record is the furthest GREEN row.",
          "id": 12,
          "links": [],
          "title": "Furthest Station (DX)",
          "vizConfig": {
            "group": "table",
            "kind": "VizConfig",
            "spec": {
              "fieldConfig": {
                "defaults": {
                  "color": {
                    "mode": "thresholds"
                  },
                  "custom": {
                    "align": "auto",
                    "cellOptions": {
                      "type": "auto"
                    },
                    "inspect": false
                  }
                },
                "overrides": [
                  {
                    "matcher": {
                      "id": "byName",
                      "options": "distance_m"
                    },
                    "properties": [
                      {
                        "id": "unit",
                        "value": "lengthm"
                      },
                      {
                        "id": "custom.cellOptions",
                        "value": {
                          "type": "color-background",
                          "mode": "gradient"
                        }
                      },
                      {
                        "id": "thresholds",
                        "value": {
                          "mode": "absolute",
                          "steps": [
                            {
                              "color": "blue",
                              "value": null
                            },
                            {
                              "color": "green",
                              "value": 20000
                            },
                            {
                              "color": "yellow",
                              "value": 50000
                            }
                          ]
                        }
                      }
                    ]
                  },
                  {
                    "matcher": {
                      "id": "byName",
                      "options": "rf"
                    },
                    "properties": [
                      {
                        "id": "custom.cellOptions",
                        "value": {
                          "type": "color-background",
                          "mode": "basic"
                        }
                      },
                      {
                        "id": "mappings",
                        "value": [
                          {
                            "type": "value",
                            "options": {
                              "direct": {
                                "color": "green",
                                "index": 0
                              },
                              "via digi": {
                                "color": "orange",
                                "index": 1
                              }
                            }
                          }
                        ]
                      }
                    ]
                  }
                ]
              },
              "options": {
                "cellHeight": "sm",
                "footer": {
                  "show": false
                },
                "showHeader": true
              }
            },
            "version": "13.0.1+security-01"
          }
        }
      },
      "panel-13": {
        "kind": "Panel",
        "spec": {
          "data": {
            "kind": "QueryGroup",
            "spec": {
              "queries": [
                {
                  "kind": "PanelQuery",
                  "spec": {
                    "hidden": false,
                    "query": {
                      "datasource": {
                        "name": "aprs_influxdb"
                      },
                      "group": "influxdb",
                      "kind": "DataQuery",
                      "spec": {
                        "query": "import \"math\"\n\nR = 6371000.0  // Earth radius in METRES\ntoRad = (deg) => deg * math.pi / 180.0\n// APRS stores coords as DDMM.mm (deg + decimal minutes); convert to decimal degrees\ntoDeg = (v) => {\n    s = if v < 0.0 then -1.0 else 1.0\n    a = v * s\n    d = float(v: int(v: a / 100.0))\n    return s * (d + (a - d * 100.0) / 60.0)\n}\nfixLat = (lat) => if (if lat < 0.0 then lat * -1.0 else lat) > 90.0 then toDeg(v: lat) else lat\nfixLon = (lat, lon) => if (if lat < 0.0 then lat * -1.0 else lat) > 90.0 then toDeg(v: lon) else lon\n\n// Gateway position = latest lat/lon reported by the \"My Callsign\" station.\ngw = from(bucket: \"${bucket}\")\n  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n  |> filter(fn: (r) => r[\"_measurement\"] == \"aprs_syslog\")\n  |> filter(fn: (r) => r[\"callsign\"] == \"${bucket}\")\n  |> filter(fn: (r) => r[\"_field\"] == \"lat\" or r[\"_field\"] == \"lon\")\n  |> filter(fn: (r) => r._value != 0.0)\n  |> group(columns: [\"_field\"])\n  |> last()\n  |> keep(columns: [\"_field\", \"_value\"])\n  |> group()\n  |> map(fn: (r) => ({_field: r._field, _value: r._value, _key: \"gw\"}))\n  |> pivot(rowKey: [\"_key\"], columnKey: [\"_field\"], valueColumn: \"_value\")\n  |> findRecord(fn: (key) => true, idx: 0)\n\ngwLat = fixLat(lat: gw.lat)\ngwLon = fixLon(lat: gw.lat, lon: gw.lon)\n\nbase = from(bucket: \"${bucket}\")\n  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n  |> filter(fn: (r) => r[\"_measurement\"] == \"aprs_syslog\")\n  |> filter(fn: (r) => r[\"callsign\"] =~ /^(${station_filter:regex})$/)\n  |> filter(fn: (r) => r[\"callsign\"] != \"${bucket}\")\n  |> filter(fn: (r) => r[\"callsign\"] != \"unknown\")\n\ndist = base\n  |> filter(fn: (r) => r[\"_field\"] == \"lat\" or r[\"_field\"] == \"lon\")\n  |> filter(fn: (r) => r._value != 0.0)\n  |> group(columns: [\"callsign\", \"_field\"])\n  |> last()\n  |> keep(columns: [\"callsign\", \"_field\", \"_value\"])\n  |> group()\n  |> pivot(rowKey: [\"callsign\"], columnKey: [\"_field\"], valueColumn: \"_value\")\n  |> filter(fn: (r) => exists r.lat and exists r.lon and r.lat != 0.0 and r.lon != 0.0)\n  |> map(fn: (r) => {\n      lat2 = fixLat(lat: r.lat)\n      lon2 = fixLon(lat: r.lat, lon: r.lon)\n      dLat = toRad(deg: lat2 - gwLat)\n      dLon = toRad(deg: lon2 - gwLon)\n      a = math.sin(x: dLat / 2.0) * math.sin(x: dLat / 2.0) +\n          math.cos(x: toRad(deg: gwLat)) * math.cos(x: toRad(deg: lat2)) *\n          math.sin(x: dLon / 2.0) * math.sin(x: dLon / 2.0)\n      c = 2.0 * math.atan2(y: math.sqrt(x: a), x: math.sqrt(x: 1.0 - a))\n      return {callsign: r.callsign, _field: \"distance_m\", _value: R * c}\n  })\n\nsnr = base\n  |> filter(fn: (r) => r[\"category\"] == \"RX\")\n  |> filter(fn: (r) => r[\"_field\"] == \"snr\")\n  |> group(columns: [\"callsign\"])\n  |> mean()\n  |> group()\n  |> map(fn: (r) => ({callsign: r.callsign, _field: \"snr\", _value: r._value}))\n\nunion(tables: [dist, snr])\n  |> group()\n  |> pivot(rowKey: [\"callsign\"], columnKey: [\"_field\"], valueColumn: \"_value\")\n  |> filter(fn: (r) => exists r.snr and exists r.distance_m)"
                      },
                      "version": "v0"
                    },
                    "refId": "A"
                  }
                }
              ],
              "queryOptions": {},
              "transformations": []
            }
          },
          "description": "Every dot is a station: how far away it is (X) versus how well we actually hear it (Y). The dream is a tidy cloud sloping down to the right \u2014 far away = quieter, physics doing its job. The fun is the rebels: a near station with rubbish SNR has something in the way, a far one with cracking SNR found a magic path. Distances are measured from your My Callsign position, pulled live from the DB \u2014 no coords to babysit.",
          "id": 13,
          "links": [],
          "title": "SNR vs Distance",
          "vizConfig": {
            "group": "xychart",
            "kind": "VizConfig",
            "spec": {
              "fieldConfig": {
                "defaults": {
                  "color": {
                    "mode": "palette-classic"
                  },
                  "custom": {
                    "axisBorderShow": false,
                    "axisCenteredZero": false,
                    "axisColorMode": "text",
                    "axisLabel": "",
                    "axisPlacement": "auto",
                    "hideFrom": {
                      "legend": false,
                      "tooltip": false,
                      "viz": false
                    },
                    "pointSize": {
                      "fixed": 8
                    },
                    "pointShape": "circle",
                    "pointStrokeWidth": 1,
                    "fillOpacity": 50,
                    "show": "points",
                    "scaleDistribution": {
                      "type": "linear"
                    }
                  }
                },
                "overrides": [
                  {
                    "matcher": {
                      "id": "byName",
                      "options": "distance_m"
                    },
                    "properties": [
                      {
                        "id": "unit",
                        "value": "lengthm"
                      },
                      {
                        "id": "custom.axisLabel",
                        "value": "Distance from gateway (km)"
                      }
                    ]
                  },
                  {
                    "matcher": {
                      "id": "byName",
                      "options": "snr"
                    },
                    "properties": [
                      {
                        "id": "decimals",
                        "value": 1
                      },
                      {
                        "id": "custom.axisLabel",
                        "value": "Mean SNR (dB)"
                      }
                    ]
                  }
                ]
              },
              "options": {
                "mapping": "auto",
                "series": [
                  {
                    "x": {
                      "matcher": {
                        "id": "byName",
                        "options": "distance_m"
                      }
                    },
                    "y": {
                      "matcher": {
                        "id": "byName",
                        "options": "snr"
                      }
                    }
                  }
                ],
                "legend": {
                  "calcs": [],
                  "displayMode": "list",
                  "placement": "bottom",
                  "showLegend": true
                },
                "tooltip": {
                  "mode": "single",
                  "sort": "none"
                }
              }
            },
            "version": "13.0.1+security-01"
          }
        }
      },
      "panel-14": {
        "kind": "Panel",
        "spec": {
          "data": {
            "kind": "QueryGroup",
            "spec": {
              "queries": [
                {
                  "kind": "PanelQuery",
                  "spec": {
                    "hidden": false,
                    "query": {
                      "datasource": {
                        "name": "aprs_influxdb"
                      },
                      "group": "influxdb",
                      "kind": "DataQuery",
                      "spec": {
                        "query": "from(bucket: \"${bucket}\")\n  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n  |> filter(fn: (r) => r[\"_measurement\"] == \"aprs_syslog\")\n  |> filter(fn: (r) => r[\"callsign\"] =~ /^(${station_filter:regex})$/)\n  |> filter(fn: (r) => r[\"callsign\"] != \"unknown\")\n  |> filter(fn: (r) => r[\"category\"] =~ /^(${category_filter:regex})$/)\n  |> filter(fn: (r) => r[\"_field\"] == \"message\" or r[\"_field\"] == \"snr\" or r[\"_field\"] == \"rssi\" or r[\"_field\"] == \"tocall\" or r[\"_field\"] == \"path\" or r[\"_field\"] == \"digipeated\" or r[\"_field\"] == \"distance_km\")\n  |> pivot(rowKey: [\"_time\",\"callsign\",\"category\"], columnKey: [\"_field\"], valueColumn: \"_value\")\n  |> group()\n  |> keep(columns: [\"_time\",\"callsign\",\"category\",\"tocall\",\"path\",\"digipeated\",\"distance_km\",\"snr\",\"rssi\",\"message\"])\n  |> sort(columns: [\"_time\"], desc: true)\n  |> limit(n: 200)"
                      },
                      "version": "v0"
                    },
                    "refId": "A"
                  }
                }
              ],
              "queryOptions": {},
              "transformations": []
            }
          },
          "description": "The raw syslog firehose \u2014 last 200 packets with SNR/RSSI, the iGate's own distance, device tocall, routing path and whether it arrived via a digipeater. RX = heard over RF, TX = sent, TCPIP = APRS-IS.",
          "id": 14,
          "links": [],
          "title": "Recent Packets",
          "vizConfig": {
            "group": "table",
            "kind": "VizConfig",
            "spec": {
              "fieldConfig": {
                "defaults": {
                  "color": {
                    "mode": "thresholds"
                  },
                  "custom": {
                    "align": "auto",
                    "cellOptions": {
                      "type": "auto"
                    },
                    "inspect": false,
                    "filterable": true
                  }
                },
                "overrides": [
                  {
                    "matcher": {
                      "id": "byName",
                      "options": "message"
                    },
                    "properties": [
                      {
                        "id": "custom.width",
                        "value": 380
                      }
                    ]
                  },
                  {
                    "matcher": {
                      "id": "byName",
                      "options": "snr"
                    },
                    "properties": [
                      {
                        "id": "custom.cellOptions",
                        "value": {
                          "type": "color-background",
                          "mode": "basic"
                        }
                      },
                      {
                        "id": "thresholds",
                        "value": {
                          "mode": "absolute",
                          "steps": [
                            {
                              "color": "red",
                              "value": null
                            },
                            {
                              "color": "orange",
                              "value": -18
                            },
                            {
                              "color": "#EAB839",
                              "value": -12
                            },
                            {
                              "color": "green",
                              "value": -3
                            }
                          ]
                        }
                      },
                      {
                        "id": "unit",
                        "value": "dB"
                      },
                      {
                        "id": "custom.width",
                        "value": 70
                      }
                    ]
                  },
                  {
                    "matcher": {
                      "id": "byName",
                      "options": "rssi"
                    },
                    "properties": [
                      {
                        "id": "unit",
                        "value": "dBm"
                      },
                      {
                        "id": "custom.width",
                        "value": 70
                      }
                    ]
                  },
                  {
                    "matcher": {
                      "id": "byName",
                      "options": "distance_km"
                    },
                    "properties": [
                      {
                        "id": "unit",
                        "value": "lengthkm"
                      },
                      {
                        "id": "decimals",
                        "value": 1
                      },
                      {
                        "id": "custom.width",
                        "value": 85
                      }
                    ]
                  },
                  {
                    "matcher": {
                      "id": "byName",
                      "options": "digipeated"
                    },
                    "properties": [
                      {
                        "id": "custom.width",
                        "value": 90
                      },
                      {
                        "id": "custom.cellOptions",
                        "value": {
                          "type": "color-background",
                          "mode": "basic"
                        }
                      },
                      {
                        "id": "mappings",
                        "value": [
                          {
                            "type": "value",
                            "options": {
                              "0": {
                                "text": "direct",
                                "color": "green",
                                "index": 0
                              },
                              "1": {
                                "text": "digi",
                                "color": "orange",
                                "index": 1
                              }
                            }
                          }
                        ]
                      },
                      {
                        "id": "thresholds",
                        "value": {
                          "mode": "absolute",
                          "steps": [
                            {
                              "color": "green",
                              "value": null
                            },
                            {
                              "color": "orange",
                              "value": 1
                            }
                          ]
                        }
                      }
                    ]
                  },
                  {
                    "matcher": {
                      "id": "byName",
                      "options": "tocall"
                    },
                    "properties": [
                      {
                        "id": "custom.width",
                        "value": 85
                      }
                    ]
                  },
                  {
                    "matcher": {
                      "id": "byName",
                      "options": "category"
                    },
                    "properties": [
                      {
                        "id": "custom.width",
                        "value": 70
                      }
                    ]
                  }
                ]
              },
              "options": {
                "cellHeight": "sm",
                "footer": {
                  "show": false
                },
                "showHeader": true,
                "sortBy": [
                  {
                    "displayName": "Time",
                    "desc": true
                  }
                ]
              }
            },
            "version": "13.0.1+security-01"
          }
        }
      },
      "panel-15": {
        "kind": "Panel",
        "spec": {
          "data": {
            "kind": "QueryGroup",
            "spec": {
              "queries": [
                {
                  "kind": "PanelQuery",
                  "spec": {
                    "hidden": false,
                    "query": {
                      "datasource": {
                        "name": "aprs_influxdb"
                      },
                      "group": "influxdb",
                      "kind": "DataQuery",
                      "spec": {
                        "query": "import \"math\"\ntoDeg = (v) => { s = if v < 0.0 then -1.0 else 1.0\n  a = v * s\n  d = float(v: int(v: a / 100.0))\n  return s * (d + (a - d * 100.0) / 60.0) }\nfixLat = (lat) => if (if lat < 0.0 then lat * -1.0 else lat) > 90.0 then toDeg(v: lat) else lat\nfixLon = (lat, lon) => if (if lat < 0.0 then lat * -1.0 else lat) > 90.0 then toDeg(v: lon) else lon\nfrom(bucket: \"${bucket}\")\n  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n  |> filter(fn: (r) => r[\"category\"] == \"RX\")\n  |> filter(fn: (r) => r[\"callsign\"] =~ /^(${station_filter:regex})$/)\n  |> filter(fn: (r) => r[\"callsign\"] != \"${bucket}\" and r[\"callsign\"] != \"unknown\")\n  |> filter(fn: (r) => r[\"_field\"] == \"lat\" or r[\"_field\"] == \"lon\" or r[\"_field\"] == \"snr\")\n  |> pivot(rowKey: [\"_time\",\"callsign\"], columnKey: [\"_field\"], valueColumn: \"_value\")\n  |> filter(fn: (r) => exists r.lat and exists r.lon and r.lat != 0.0 and r.lon != 0.0)\n  |> map(fn: (r) => ({_time: r._time, lat: fixLat(lat: r.lat), lon: fixLon(lat: r.lat, lon: r.lon), snr: if exists r.snr then r.snr else 0.0}))\n  |> keep(columns: [\"_time\",\"lat\",\"lon\",\"snr\"])"
                      },
                      "version": "v0"
                    },
                    "refId": "A"
                  }
                },
                {
                  "kind": "PanelQuery",
                  "spec": {
                    "hidden": false,
                    "query": {
                      "datasource": {
                        "name": "aprs_influxdb"
                      },
                      "group": "influxdb",
                      "kind": "DataQuery",
                      "spec": {
                        "query": "import \"math\"\ntoDeg = (v) => { s = if v < 0.0 then -1.0 else 1.0\n  a = v * s\n  d = float(v: int(v: a / 100.0))\n  return s * (d + (a - d * 100.0) / 60.0) }\nfixLat = (lat) => if (if lat < 0.0 then lat * -1.0 else lat) > 90.0 then toDeg(v: lat) else lat\nfixLon = (lat, lon) => if (if lat < 0.0 then lat * -1.0 else lat) > 90.0 then toDeg(v: lon) else lon\nfrom(bucket: \"${bucket}\")\n  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n  |> filter(fn: (r) => r[\"callsign\"] == \"${bucket}\")\n  |> filter(fn: (r) => r[\"_field\"] == \"lat\" or r[\"_field\"] == \"lon\")\n  |> filter(fn: (r) => r._value != 0.0)\n  |> group(columns: [\"_field\"]) |> last() |> keep(columns: [\"_field\",\"_value\"])\n  |> map(fn: (r) => ({_field: r._field, _value: r._value, _key: \"gw\"}))\n  |> group()\n  |> pivot(rowKey: [\"_key\"], columnKey: [\"_field\"], valueColumn: \"_value\")\n  |> map(fn: (r) => ({lat: fixLat(lat: r.lat), lon: fixLon(lat: r.lat, lon: r.lon)}))"
                      },
                      "version": "v0"
                    },
                    "refId": "B"
                  }
                }
              ],
              "queryOptions": {},
              "transformations": []
            }
          },
          "description": "Every position this station has received, plotted as a translucent dot-cloud coloured by SNR. Dense/green = solid coverage; sparse/red-edged = the fringe of your RF reach.",
          "id": 15,
          "links": [],
          "title": "Coverage (heard positions)",
          "vizConfig": {
            "group": "geomap",
            "kind": "VizConfig",
            "version": "13.0.1+security-01",
            "spec": {
              "fieldConfig": {
                "defaults": {
                  "color": {
                    "mode": "thresholds"
                  },
                  "thresholds": {
                    "mode": "absolute",
                    "steps": [
                      {
                        "color": "red",
                        "value": null
                      },
                      {
                        "color": "orange",
                        "value": -18
                      },
                      {
                        "color": "#EAB839",
                        "value": -12
                      },
                      {
                        "color": "green",
                        "value": -3
                      }
                    ]
                  }
                },
                "overrides": []
              },
              "options": {
                "basemap": {
                  "config": {},
                  "name": "Layer 0",
                  "noRepeat": false,
                  "type": "default"
                },
                "controls": {
                  "mouseWheelZoom": true,
                  "showAttribution": true,
                  "showDebug": false,
                  "showMeasure": true,
                  "showScale": false,
                  "showZoom": true
                },
                "view": {
                  "allLayers": true,
                  "dashboardVariable": false,
                  "id": "coords",
                  "lat": 53.704424,
                  "lon": -1.942539,
                  "noRepeat": false,
                  "shared": true,
                  "zoom": 10
                },
                "tooltip": {
                  "mode": "details"
                },
                "layers": [
                  {
                    "config": {
                      "showLegend": true,
                      "style": {
                        "color": {
                          "field": "snr",
                          "fixed": "dark-green"
                        },
                        "opacity": 0.35,
                        "rotation": {
                          "fixed": 0,
                          "max": 360,
                          "min": -360,
                          "mode": "mod"
                        },
                        "size": {
                          "fixed": 4,
                          "max": 15,
                          "min": 2
                        },
                        "symbol": {
                          "fixed": "img/icons/marker/circle.svg",
                          "mode": "fixed"
                        },
                        "symbolAlign": {
                          "horizontal": "center",
                          "vertical": "center"
                        },
                        "text": {
                          "fixed": "",
                          "mode": "fixed"
                        },
                        "textConfig": {
                          "fontSize": 12,
                          "offsetX": 0,
                          "offsetY": 0,
                          "textAlign": "center",
                          "textBaseline": "middle"
                        }
                      }
                    },
                    "filterData": {
                      "id": "byRefId",
                      "options": "A"
                    },
                    "location": {
                      "latitude": "lat",
                      "longitude": "lon",
                      "mode": "coords"
                    },
                    "name": "Coverage",
                    "tooltip": false,
                    "type": "markers"
                  },
                  {
                    "config": {
                      "showLegend": true,
                      "style": {
                        "color": {
                          "fixed": "red"
                        },
                        "opacity": 1,
                        "rotation": {
                          "fixed": 0,
                          "max": 360,
                          "min": -360,
                          "mode": "mod"
                        },
                        "size": {
                          "fixed": 11,
                          "max": 15,
                          "min": 2
                        },
                        "symbol": {
                          "fixed": "img/icons/marker/square.svg",
                          "mode": "fixed"
                        },
                        "symbolAlign": {
                          "horizontal": "center",
                          "vertical": "center"
                        },
                        "text": {
                          "fixed": "GATEWAY",
                          "mode": "fixed"
                        },
                        "textConfig": {
                          "fontSize": 14,
                          "fontWeight": "bold",
                          "offsetX": 0,
                          "offsetY": 20,
                          "textAlign": "center",
                          "textBaseline": "middle"
                        }
                      }
                    },
                    "filterData": {
                      "id": "byRefId",
                      "options": "B"
                    },
                    "layer-tooltip": true,
                    "location": {
                      "latitude": "lat",
                      "longitude": "lon",
                      "mode": "coords"
                    },
                    "name": "Gateway",
                    "tooltip": true,
                    "type": "markers"
                  }
                ]
              }
            }
          }
        }
      },
      "panel-16": {
        "kind": "Panel",
        "spec": {
          "data": {
            "kind": "QueryGroup",
            "spec": {
              "queries": [
                {
                  "kind": "PanelQuery",
                  "spec": {
                    "hidden": false,
                    "query": {
                      "datasource": {
                        "name": "aprs_influxdb"
                      },
                      "group": "influxdb",
                      "kind": "DataQuery",
                      "spec": {
                        "query": "import \"math\"\nimport \"array\"\ntoDeg = (v) => { s = if v < 0.0 then -1.0 else 1.0\n  a = v * s\n  d = float(v: int(v: a / 100.0))\n  return s * (d + (a - d * 100.0) / 60.0) }\nfixLat = (lat) => if (if lat < 0.0 then lat * -1.0 else lat) > 90.0 then toDeg(v: lat) else lat\nfixLon = (lat, lon) => if (if lat < 0.0 then lat * -1.0 else lat) > 90.0 then toDeg(v: lon) else lon\ngwBase = from(bucket: \"${bucket}\")\n  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n  |> filter(fn: (r) => r[\"callsign\"] == \"${bucket}\")\n  |> filter(fn: (r) => r[\"_field\"] == \"lat\" or r[\"_field\"] == \"lon\")\n  |> filter(fn: (r) => r._value != 0.0)\n  |> group(columns: [\"_field\"]) |> last() |> keep(columns: [\"_field\",\"_value\"]) |> group()\n  |> map(fn: (r) => ({_field: r._field, _value: r._value, _key: \"gw\"}))\n  |> pivot(rowKey: [\"_key\"], columnKey: [\"_field\"], valueColumn: \"_value\")\ngw = union(tables: [gwBase, array.from(rows: [{_key: \"~none\", lat: 0.0, lon: 0.0}])])\n  |> group() |> sort(columns: [\"_key\"]) |> findRecord(fn: (key) => true, idx: 0)\ngwLat = fixLat(lat: gw.lat)\ngwLon = fixLon(lat: gw.lat, lon: gw.lon)\n\npos = from(bucket: \"${bucket}\")\n  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n  |> filter(fn: (r) => r[\"category\"] == \"RX\")\n  |> filter(fn: (r) => r[\"callsign\"] =~ /^(${station_filter:regex})$/)\n  |> filter(fn: (r) => r[\"callsign\"] != \"${bucket}\" and r[\"callsign\"] != \"unknown\")\n  |> filter(fn: (r) => r[\"_field\"] == \"lat\" or r[\"_field\"] == \"lon\" or r[\"_field\"] == \"snr\" or r[\"_field\"] == \"digipeated\")\n  |> pivot(rowKey: [\"_time\",\"callsign\"], columnKey: [\"_field\"], valueColumn: \"_value\")\n  |> filter(fn: (r) => exists r.lat and exists r.lon and r.lat != 0.0 and r.lon != 0.0)\n  |> group(columns: [\"callsign\"])\n  |> last(column: \"_time\")\n  |> filter(fn: (r) => not exists r.digipeated or r.digipeated == 0.0)\n\nstationPts = pos |> map(fn: (r) => ({_time: r._time, lineid: r.callsign, ord: 1, lat: fixLat(lat: r.lat), lon: fixLon(lat: r.lat, lon: r.lon), snr: if exists r.snr then r.snr else 0.0}))\ngwPts = pos |> map(fn: (r) => ({_time: time(v: uint(v: r._time) - uint(v: 1000000000)), lineid: r.callsign, ord: 0, lat: gwLat, lon: gwLon, snr: if exists r.snr then r.snr else 0.0}))\nunion(tables: [gwPts, stationPts])\n  |> group()\n  |> filter(fn: (r) => gwLat != 0.0)\n  |> sort(columns: [\"_time\"])\n  |> keep(columns: [\"_time\",\"lineid\",\"ord\",\"lat\",\"lon\",\"snr\"])"
                      },
                      "version": "v0"
                    },
                    "refId": "A"
                  }
                },
                {
                  "kind": "PanelQuery",
                  "spec": {
                    "hidden": false,
                    "query": {
                      "datasource": {
                        "name": "aprs_influxdb"
                      },
                      "group": "influxdb",
                      "kind": "DataQuery",
                      "spec": {
                        "query": "import \"math\"\nimport \"array\"\ntoDeg = (v) => { s = if v < 0.0 then -1.0 else 1.0\n  a = v * s\n  d = float(v: int(v: a / 100.0))\n  return s * (d + (a - d * 100.0) / 60.0) }\nfixLat = (lat) => if (if lat < 0.0 then lat * -1.0 else lat) > 90.0 then toDeg(v: lat) else lat\nfixLon = (lat, lon) => if (if lat < 0.0 then lat * -1.0 else lat) > 90.0 then toDeg(v: lon) else lon\ngwBase = from(bucket: \"${bucket}\")\n  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n  |> filter(fn: (r) => r[\"callsign\"] == \"${bucket}\")\n  |> filter(fn: (r) => r[\"_field\"] == \"lat\" or r[\"_field\"] == \"lon\")\n  |> filter(fn: (r) => r._value != 0.0)\n  |> group(columns: [\"_field\"]) |> last() |> keep(columns: [\"_field\",\"_value\"]) |> group()\n  |> map(fn: (r) => ({_field: r._field, _value: r._value, _key: \"gw\"}))\n  |> pivot(rowKey: [\"_key\"], columnKey: [\"_field\"], valueColumn: \"_value\")\ngw = union(tables: [gwBase, array.from(rows: [{_key: \"~none\", lat: 0.0, lon: 0.0}])])\n  |> group() |> sort(columns: [\"_key\"]) |> findRecord(fn: (key) => true, idx: 0)\ngwLat = fixLat(lat: gw.lat)\ngwLon = fixLon(lat: gw.lat, lon: gw.lon)\n\npos = from(bucket: \"${bucket}\")\n  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n  |> filter(fn: (r) => r[\"category\"] == \"RX\")\n  |> filter(fn: (r) => r[\"callsign\"] =~ /^(${station_filter:regex})$/)\n  |> filter(fn: (r) => r[\"callsign\"] != \"${bucket}\" and r[\"callsign\"] != \"unknown\")\n  |> filter(fn: (r) => r[\"_field\"] == \"lat\" or r[\"_field\"] == \"lon\" or r[\"_field\"] == \"snr\" or r[\"_field\"] == \"digipeated\")\n  |> pivot(rowKey: [\"_time\",\"callsign\"], columnKey: [\"_field\"], valueColumn: \"_value\")\n  |> filter(fn: (r) => exists r.lat and exists r.lon and r.lat != 0.0 and r.lon != 0.0)\n  |> group(columns: [\"callsign\"])\n  |> last(column: \"_time\")\n  |> filter(fn: (r) => exists r.digipeated and r.digipeated == 1.0)\n\nstationPts = pos |> map(fn: (r) => ({_time: r._time, lineid: r.callsign, ord: 1, lat: fixLat(lat: r.lat), lon: fixLon(lat: r.lat, lon: r.lon), snr: if exists r.snr then r.snr else 0.0}))\ngwPts = pos |> map(fn: (r) => ({_time: time(v: uint(v: r._time) - uint(v: 1000000000)), lineid: r.callsign, ord: 0, lat: gwLat, lon: gwLon, snr: if exists r.snr then r.snr else 0.0}))\nunion(tables: [gwPts, stationPts])\n  |> group()\n  |> filter(fn: (r) => gwLat != 0.0)\n  |> sort(columns: [\"_time\"])\n  |> keep(columns: [\"_time\",\"lineid\",\"ord\",\"lat\",\"lon\",\"snr\"])"
                      },
                      "version": "v0"
                    },
                    "refId": "B"
                  }
                },
                {
                  "kind": "PanelQuery",
                  "spec": {
                    "hidden": false,
                    "query": {
                      "datasource": {
                        "name": "aprs_influxdb"
                      },
                      "group": "influxdb",
                      "kind": "DataQuery",
                      "spec": {
                        "query": "import \"math\"\ntoDeg = (v) => { s = if v < 0.0 then -1.0 else 1.0\n  a = v * s\n  d = float(v: int(v: a / 100.0))\n  return s * (d + (a - d * 100.0) / 60.0) }\nfixLat = (lat) => if (if lat < 0.0 then lat * -1.0 else lat) > 90.0 then toDeg(v: lat) else lat\nfixLon = (lat, lon) => if (if lat < 0.0 then lat * -1.0 else lat) > 90.0 then toDeg(v: lon) else lon\nfrom(bucket: \"${bucket}\")\n  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n  |> filter(fn: (r) => r[\"callsign\"] == \"${bucket}\")\n  |> filter(fn: (r) => r[\"_field\"] == \"lat\" or r[\"_field\"] == \"lon\")\n  |> filter(fn: (r) => r._value != 0.0)\n  |> group(columns: [\"_field\"]) |> last() |> keep(columns: [\"_field\",\"_value\"])\n  |> map(fn: (r) => ({_field: r._field, _value: r._value, _key: \"gw\"}))\n  |> group()\n  |> pivot(rowKey: [\"_key\"], columnKey: [\"_field\"], valueColumn: \"_value\")\n  |> map(fn: (r) => ({lat: fixLat(lat: r.lat), lon: fixLon(lat: r.lat, lon: r.lon)}))"
                      },
                      "version": "v0"
                    },
                    "refId": "C"
                  }
                }
              ],
              "queryOptions": {},
              "transformations": []
            }
          },
          "description": "Lines from the gateway to each station's last position. Direct RF contacts are coloured by SNR (green strong -> red weak); stations whose latest packet arrived via a digipeater are grey.",
          "id": 16,
          "links": [],
          "title": "Reception Lines (SNR)",
          "vizConfig": {
            "group": "geomap",
            "kind": "VizConfig",
            "version": "13.0.1+security-01",
            "spec": {
              "fieldConfig": {
                "defaults": {
                  "color": {
                    "mode": "thresholds"
                  },
                  "thresholds": {
                    "mode": "absolute",
                    "steps": [
                      {
                        "color": "red",
                        "value": null
                      },
                      {
                        "color": "orange",
                        "value": -18
                      },
                      {
                        "color": "#EAB839",
                        "value": -12
                      },
                      {
                        "color": "green",
                        "value": -3
                      }
                    ]
                  }
                },
                "overrides": []
              },
              "options": {
                "basemap": {
                  "config": {},
                  "name": "Layer 0",
                  "noRepeat": false,
                  "type": "default"
                },
                "controls": {
                  "mouseWheelZoom": true,
                  "showAttribution": true,
                  "showDebug": false,
                  "showMeasure": true,
                  "showScale": false,
                  "showZoom": true
                },
                "view": {
                  "allLayers": true,
                  "dashboardVariable": false,
                  "id": "coords",
                  "lat": 53.704424,
                  "lon": -1.942539,
                  "noRepeat": false,
                  "shared": true,
                  "zoom": 10
                },
                "tooltip": {
                  "mode": "details"
                },
                "layers": [
                  {
                    "config": {
                      "showLegend": true,
                      "style": {
                        "color": {
                          "field": "snr",
                          "fixed": "dark-green"
                        },
                        "opacity": 0.9,
                        "lineWidth": 3,
                        "size": {
                          "fixed": 5,
                          "max": 15,
                          "min": 2
                        },
                        "symbol": {
                          "fixed": "img/icons/marker/circle.svg",
                          "mode": "fixed"
                        },
                        "symbolAlign": {
                          "horizontal": "center",
                          "vertical": "center"
                        },
                        "text": {
                          "field": "lineid",
                          "fixed": "",
                          "mode": "field"
                        },
                        "textConfig": {
                          "fontSize": 11,
                          "offsetX": 0,
                          "offsetY": 12,
                          "textAlign": "center",
                          "textBaseline": "middle"
                        }
                      },
                      "arrow": 0
                    },
                    "filterData": {
                      "id": "byRefId",
                      "options": "A"
                    },
                    "location": {
                      "latitude": "lat",
                      "longitude": "lon",
                      "mode": "coords"
                    },
                    "name": "Direct RF (SNR)",
                    "tooltip": true,
                    "type": "route"
                  },
                  {
                    "config": {
                      "showLegend": true,
                      "style": {
                        "color": {
                          "fixed": "#888888"
                        },
                        "opacity": 0.9,
                        "lineWidth": 3,
                        "size": {
                          "fixed": 5,
                          "max": 15,
                          "min": 2
                        },
                        "symbol": {
                          "fixed": "img/icons/marker/circle.svg",
                          "mode": "fixed"
                        },
                        "symbolAlign": {
                          "horizontal": "center",
                          "vertical": "center"
                        },
                        "text": {
                          "field": "lineid",
                          "fixed": "",
                          "mode": "fixed"
                        },
                        "textConfig": {
                          "fontSize": 11,
                          "offsetX": 0,
                          "offsetY": 12,
                          "textAlign": "center",
                          "textBaseline": "middle"
                        }
                      },
                      "arrow": 0
                    },
                    "filterData": {
                      "id": "byRefId",
                      "options": "B"
                    },
                    "location": {
                      "latitude": "lat",
                      "longitude": "lon",
                      "mode": "coords"
                    },
                    "name": "Via digipeater",
                    "tooltip": true,
                    "type": "route"
                  },
                  {
                    "config": {
                      "showLegend": true,
                      "style": {
                        "color": {
                          "fixed": "red"
                        },
                        "opacity": 1,
                        "rotation": {
                          "fixed": 0,
                          "max": 360,
                          "min": -360,
                          "mode": "mod"
                        },
                        "size": {
                          "fixed": 11,
                          "max": 15,
                          "min": 2
                        },
                        "symbol": {
                          "fixed": "img/icons/marker/square.svg",
                          "mode": "fixed"
                        },
                        "symbolAlign": {
                          "horizontal": "center",
                          "vertical": "center"
                        },
                        "text": {
                          "fixed": "GATEWAY",
                          "mode": "fixed"
                        },
                        "textConfig": {
                          "fontSize": 14,
                          "fontWeight": "bold",
                          "offsetX": 0,
                          "offsetY": 20,
                          "textAlign": "center",
                          "textBaseline": "middle"
                        }
                      }
                    },
                    "filterData": {
                      "id": "byRefId",
                      "options": "C"
                    },
                    "layer-tooltip": true,
                    "location": {
                      "latitude": "lat",
                      "longitude": "lon",
                      "mode": "coords"
                    },
                    "name": "Gateway",
                    "tooltip": true,
                    "type": "markers"
                  }
                ]
              }
            }
          }
        }
      },
      "panel-h1": {
        "kind": "Panel",
        "spec": {
          "data": {
            "kind": "QueryGroup",
            "spec": {
              "queries": [],
              "queryOptions": {},
              "transformations": []
            }
          },
          "description": "",
          "id": 902,
          "links": [],
          "title": "",
          "vizConfig": {
            "group": "text",
            "kind": "VizConfig",
            "version": "13.0.1+security-01",
            "spec": {
              "fieldConfig": {
                "defaults": {},
                "overrides": []
              },
              "options": {
                "content": "### \ud83d\uddfa\ufe0f Maps & Coverage",
                "mode": "markdown"
              }
            }
          }
        }
      },
      "panel-h2": {
        "kind": "Panel",
        "spec": {
          "data": {
            "kind": "QueryGroup",
            "spec": {
              "queries": [],
              "queryOptions": {},
              "transformations": []
            }
          },
          "description": "",
          "id": 903,
          "links": [],
          "title": "",
          "vizConfig": {
            "group": "text",
            "kind": "VizConfig",
            "version": "13.0.1+security-01",
            "spec": {
              "fieldConfig": {
                "defaults": {},
                "overrides": []
              },
              "options": {
                "content": "### \ud83d\udce1 Activity",
                "mode": "markdown"
              }
            }
          }
        }
      },
      "panel-h3": {
        "kind": "Panel",
        "spec": {
          "data": {
            "kind": "QueryGroup",
            "spec": {
              "queries": [],
              "queryOptions": {},
              "transformations": []
            }
          },
          "description": "",
          "id": 904,
          "links": [],
          "title": "",
          "vizConfig": {
            "group": "text",
            "kind": "VizConfig",
            "version": "13.0.1+security-01",
            "spec": {
              "fieldConfig": {
                "defaults": {},
                "overrides": []
              },
              "options": {
                "content": "### \ud83d\udcf6 Signal & RF",
                "mode": "markdown"
              }
            }
          }
        }
      },
      "panel-h4": {
        "kind": "Panel",
        "spec": {
          "data": {
            "kind": "QueryGroup",
            "spec": {
              "queries": [],
              "queryOptions": {},
              "transformations": []
            }
          },
          "description": "",
          "id": 905,
          "links": [],
          "title": "",
          "vizConfig": {
            "group": "text",
            "kind": "VizConfig",
            "version": "13.0.1+security-01",
            "spec": {
              "fieldConfig": {
                "defaults": {},
                "overrides": []
              },
              "options": {
                "content": "### \ud83d\udd0b Telemetry",
                "mode": "markdown"
              }
            }
          }
        }
      },
      "panel-h5": {
        "kind": "Panel",
        "spec": {
          "data": {
            "kind": "QueryGroup",
            "spec": {
              "queries": [],
              "queryOptions": {},
              "transformations": []
            }
          },
          "description": "",
          "id": 906,
          "links": [],
          "title": "",
          "vizConfig": {
            "group": "text",
            "kind": "VizConfig",
            "version": "13.0.1+security-01",
            "spec": {
              "fieldConfig": {
                "defaults": {},
                "overrides": []
              },
              "options": {
                "content": "### \ud83d\udcdc Logs",
                "mode": "markdown"
              }
            }
          }
        }
      }
    },
    "layout": {
      "kind": "GridLayout",
      "spec": {
        "items": [
          {
            "kind": "GridLayoutItem",
            "spec": {
              "element": {
                "kind": "ElementReference",
                "name": "panel-h1"
              },
              "height": 2,
              "width": 24,
              "x": 0,
              "y": 0
            }
          },
          {
            "kind": "GridLayoutItem",
            "spec": {
              "element": {
                "kind": "ElementReference",
                "name": "panel-4"
              },
              "height": 14,
              "width": 24,
              "x": 0,
              "y": 2
            }
          },
          {
            "kind": "GridLayoutItem",
            "spec": {
              "element": {
                "kind": "ElementReference",
                "name": "panel-16"
              },
              "height": 13,
              "width": 12,
              "x": 0,
              "y": 16
            }
          },
          {
            "kind": "GridLayoutItem",
            "spec": {
              "element": {
                "kind": "ElementReference",
                "name": "panel-15"
              },
              "height": 13,
              "width": 12,
              "x": 12,
              "y": 16
            }
          },
          {
            "kind": "GridLayoutItem",
            "spec": {
              "element": {
                "kind": "ElementReference",
                "name": "panel-6"
              },
              "height": 13,
              "width": 24,
              "x": 0,
              "y": 29
            }
          },
          {
            "kind": "GridLayoutItem",
            "spec": {
              "element": {
                "kind": "ElementReference",
                "name": "panel-h2"
              },
              "height": 2,
              "width": 24,
              "x": 0,
              "y": 42
            }
          },
          {
            "kind": "GridLayoutItem",
            "spec": {
              "element": {
                "kind": "ElementReference",
                "name": "panel-1"
              },
              "height": 8,
              "width": 12,
              "x": 0,
              "y": 44
            }
          },
          {
            "kind": "GridLayoutItem",
            "spec": {
              "element": {
                "kind": "ElementReference",
                "name": "panel-9"
              },
              "height": 8,
              "width": 12,
              "x": 12,
              "y": 44
            }
          },
          {
            "kind": "GridLayoutItem",
            "spec": {
              "element": {
                "kind": "ElementReference",
                "name": "panel-7"
              },
              "height": 8,
              "width": 24,
              "x": 0,
              "y": 52
            }
          },
          {
            "kind": "GridLayoutItem",
            "spec": {
              "element": {
                "kind": "ElementReference",
                "name": "panel-11"
              },
              "height": 8,
              "width": 24,
              "x": 0,
              "y": 60
            }
          },
          {
            "kind": "GridLayoutItem",
            "spec": {
              "element": {
                "kind": "ElementReference",
                "name": "panel-h3"
              },
              "height": 2,
              "width": 24,
              "x": 0,
              "y": 68
            }
          },
          {
            "kind": "GridLayoutItem",
            "spec": {
              "element": {
                "kind": "ElementReference",
                "name": "panel-2"
              },
              "height": 9,
              "width": 24,
              "x": 0,
              "y": 70
            }
          },
          {
            "kind": "GridLayoutItem",
            "spec": {
              "element": {
                "kind": "ElementReference",
                "name": "panel-8"
              },
              "height": 8,
              "width": 12,
              "x": 0,
              "y": 79
            }
          },
          {
            "kind": "GridLayoutItem",
            "spec": {
              "element": {
                "kind": "ElementReference",
                "name": "panel-13"
              },
              "height": 9,
              "width": 12,
              "x": 12,
              "y": 79
            }
          },
          {
            "kind": "GridLayoutItem",
            "spec": {
              "element": {
                "kind": "ElementReference",
                "name": "panel-12"
              },
              "height": 9,
              "width": 12,
              "x": 0,
              "y": 88
            }
          },
          {
            "kind": "GridLayoutItem",
            "spec": {
              "element": {
                "kind": "ElementReference",
                "name": "panel-10"
              },
              "height": 8,
              "width": 12,
              "x": 12,
              "y": 88
            }
          },
          {
            "kind": "GridLayoutItem",
            "spec": {
              "element": {
                "kind": "ElementReference",
                "name": "panel-h4"
              },
              "height": 2,
              "width": 24,
              "x": 0,
              "y": 97
            }
          },
          {
            "kind": "GridLayoutItem",
            "spec": {
              "element": {
                "kind": "ElementReference",
                "name": "panel-5"
              },
              "height": 8,
              "width": 24,
              "x": 0,
              "y": 99
            }
          },
          {
            "kind": "GridLayoutItem",
            "spec": {
              "element": {
                "kind": "ElementReference",
                "name": "panel-h5"
              },
              "height": 2,
              "width": 24,
              "x": 0,
              "y": 107
            }
          },
          {
            "kind": "GridLayoutItem",
            "spec": {
              "element": {
                "kind": "ElementReference",
                "name": "panel-14"
              },
              "height": 12,
              "width": 24,
              "x": 0,
              "y": 109
            }
          }
        ]
      }
    },
    "links": [],
    "liveNow": false,
    "preload": false,
    "tags": [],
    "timeSettings": {
      "autoRefresh": "30s",
      "autoRefreshIntervals": [
        "5s",
        "10s",
        "30s",
        "1m",
        "5m",
        "15m",
        "30m",
        "1h",
        "2h",
        "1d"
      ],
      "fiscalYearStartMonth": 0,
      "from": "now-12h",
      "hideTimepicker": false,
      "timezone": "browser",
      "to": "now"
    },
    "title": "LoRa-APRS iGate Monitor",
    "variables": [
      {
        "kind": "CustomVariable",
        "spec": {
          "allowCustomValue": true,
          "current": {
            "text": [
              "N0CALL-10"
            ],
            "value": [
              "N0CALL-10"
            ]
          },
          "description": "InfluxDB bucket this dashboard reads from. When cloning this dashboard for another operator, change ONLY this value.",
          "hide": "hideVariable",
          "includeAll": false,
          "label": "Bucket",
          "multi": false,
          "name": "bucket",
          "options": [
            {
              "selected": true,
              "text": "N0CALL-10",
              "value": "N0CALL-10"
            }
          ],
          "query": "N0CALL-10",
          "skipUrlSync": false,
          "valuesFormat": "csv"
        }
      },
      {
        "kind": "CustomVariable",
        "spec": {
          "allowCustomValue": false,
          "current": {
            "text": [
              "rssi",
              "snr"
            ],
            "value": [
              "rssi",
              "snr"
            ]
          },
          "description": "Select which link quality fields (RSSI, SNR) to display.",
          "hide": "dontHide",
          "includeAll": true,
          "label": "Link Quality Fields",
          "multi": true,
          "name": "link_quality_fields",
          "options": [],
          "query": "rssi,snr",
          "skipUrlSync": false,
          "valuesFormat": "csv"
        }
      },
      {
        "kind": "CustomVariable",
        "spec": {
          "allowCustomValue": false,
          "current": {
            "text": [
              "RX"
            ],
            "value": [
              "RX"
            ]
          },
          "description": "This filter lets you differentiate between RF, Internet, and System data",
          "hide": "dontHide",
          "includeAll": true,
          "label": "Data Sources",
          "multi": true,
          "name": "category_filter",
          "options": [],
          "query": "RX,TX,TCPIP,SYSTEM,CRC_ERROR",
          "skipUrlSync": false,
          "valuesFormat": "csv"
        }
      },
      {
        "kind": "QueryVariable",
        "spec": {
          "allowCustomValue": true,
          "current": {
            "text": "All",
            "value": [
              "$__all"
            ]
          },
          "definition": "import \"influxdata/influxdb/schema\"\nschema.tagValues(bucket: \"${bucket}\", tag: \"callsign\")",
          "description": "This should filter by specific callsign, as found in the database.",
          "hide": "dontHide",
          "includeAll": true,
          "label": "Station ID / Callsign",
          "multi": true,
          "name": "station_filter",
          "options": [],
          "query": {
            "datasource": {
              "name": "aprs_influxdb"
            },
            "group": "influxdb",
            "kind": "DataQuery",
            "spec": {
              "query": "import \"influxdata/influxdb/schema\"\nschema.tagValues(bucket: \"${bucket}\", tag: \"callsign\")"
            },
            "version": "v0"
          },
          "refresh": "onDashboardLoad",
          "regex": "",
          "regexApplyTo": "value",
          "skipUrlSync": false,
          "sort": "disabled"
        }
      }
    ]
  }
}