This commit is contained in:
Jeffrey C. Ollie 2023-12-13 23:32:42 -06:00
commit d8f9225c34
Signed by: jeff
GPG key ID: 6F86035A6D97044E
25 changed files with 2368 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/result*

161
feeder.nix Normal file
View file

@ -0,0 +1,161 @@
{ mainConfig, feederConfig, pkgs, self, lib }: {
config = {
users.groups.${feederConfig.name} = { };
users.users.${feederConfig.name} = {
isSystemUser = true;
group = "${feederConfig.name}";
};
systemd.services."${feederConfig.name}-mlat" =
let
uuidFile = pkgs.writeTextFile {
name = "${feederConfig.name}-uuid";
text = "${feederConfig.uuid}";
};
options = [
"--input-type"
feederConfig.mlat.input.type
"--no-udp"
"--input-connect"
"${feederConfig.mlat.input.host}:${toString feederConfig.mlat.input.port}"
"--server"
feederConfig.mlat.server
"--user"
feederConfig.username
"--lat"
mainConfig.latitude
"--lon"
mainConfig.longitude
"--alt"
(toString mainConfig.altitude)
"--uuid-file"
"${uuidFile}"
] ++ (
if feederConfig.mlat.privacy
then
[
"--privacy"
]
else
[ ]
) ++ (
map (result: "--result ${result}") feederConfig.mlat.results
);
in
{
enable = feederConfig.mlat.enable;
description = "${feederConfig.name} MLAT client.";
wants = [ "network.target" ];
after = [ "network.target" ];
unitConfig = {
Documentation = "https://github.com/makrsmark/mlat-client/";
};
serviceConfig = {
Type = "simple";
User = "${feederConfig.name}";
RuntimeDirectory = "${feederConfig.name}-mlat";
RuntimeDirectoryMode = "0755";
ExecStart = "${self.packages.${pkgs.system}.adsbfi-mlat-client}/bin/mlat-client ${lib.concatStringsSep " " options}";
SyslogIdentifier = "${feederConfig.name}-mlat";
Restart = "on-failure";
RestartSec = 30;
Nice = -1;
};
wantedBy = [ "multi-user.target" ];
};
systemd.services."${feederConfig.name}-feed" =
let
uuidFile = pkgs.writeTextFile {
name = "${feederConfig.name}-uuid";
text = "${feederConfig.uuid}";
};
connectors = map
(
connector: "--net-connector ${connector}"
)
(
map
(
connector: (
lib.concatStringsSep "," (
[
connector.primary.host
(toString connector.primary.port)
connector.protocol
] ++ (
if connector.silentFail
then
[
"silent_fail"
]
else
[ ]
) ++ (
if connector.secondary != null
then
[
connector.secondary.host
(toString connector.secondary.port)
]
else
[ ]
)
)
)
)
feederConfig.feed.connectors
);
options = [
"--quiet"
"--net"
"--net-only"
"--write-json"
"%t/${feederConfig.name}-feed"
"--net-beast-reduce-interval 0.5"
"--net-heartbeat 60"
"--net-ro-size 1280"
"--net-ro-interval 0.2"
"--net-ro-port 0"
"--net-sbs-port 0"
"--net-bi-port ${toString feederConfig.feed.beastInputPort}"
"--net-bo-port 0"
"--net-ri-port 0"
"--write-json-every 1"
"--lat"
mainConfig.latitude
"--lon"
mainConfig.longitude
"--max-range 450"
"--json-location-accuracy 2"
"--range-outline-hours 24"
"--uuid-file"
"${uuidFile}"
] ++ connectors;
in
{
enable = feederConfig.feed.enable;
description = "${feederConfig.name} feeder.";
wants = [ "network.target" ];
after = [ "network.target" ];
unitConfig = {
Documentation = "https://github.com/widehopf/readsb/";
StartLimitIntervalSec = 1;
};
serviceConfig = {
Type = "simple";
User = "${feederConfig.name}";
RuntimeDirectory = "${feederConfig.name}-feed";
RuntimeDirectoryMode = "0755";
ExecStart = "${self.packages.${pkgs.system}.adsbfi-readsb}/bin/readsb ${lib.concatStringsSep " " options}";
SyslogIdentifier = "${feederConfig.name}-feed";
Restart = "on-failure";
RestartSec = 30;
StartLimitBurst = 100;
Nice = -1;
};
wantedBy = [ "multi-user.target" ];
};
};
}

60
flake.lock Normal file
View file

@ -0,0 +1,60 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1694529238,
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1697226376,
"narHash": "sha256-cumLLb1QOUtWieUnLGqo+ylNt3+fU8Lcv5Zl+tYbRUE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "898cb2064b6e98b8c5499f37e81adbdf2925f7c5",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-23.05",
"type": "indirect"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

17
flake.nix Normal file
View file

@ -0,0 +1,17 @@
{
description = "piaware";
inputs = {
nixpkgs = {
url = "nixpkgs/nixos-23.05";
};
flake-utils = {
url = "github:numtide/flake-utils";
};
};
outputs = { self, nixpkgs, flake-utils }: {
packages = (import ./packages { inherit self flake-utils nixpkgs; }).packages;
nixosModules = (import ./modules { inherit self; }).nixosModules;
};
}

823
modules/config/default.nix Normal file
View file

@ -0,0 +1,823 @@
{ self, cfg, pkgs, lib, ... }:
let
createMlatService =
(name: feedConfig:
let
uuidFile = pkgs.writeTextFile {
name = "${name}-uuid";
text = "${feedConfig.uuid}";
};
options = [
"--input-type"
feedConfig.mlat.input.type
"--no-udp"
"--input-connect"
"${feedConfig.mlat.input.host}:${toString feedConfig.mlat.input.port}"
"--server"
feedConfig.mlat.server
"--user"
feedConfig.username
"--lat"
cfg.latitude
"--lon"
cfg.longitude
"--alt"
(toString cfg.altitude)
"--uuid-file"
"${uuidFile}"
] ++ (
if feedConfig.mlat.privacy
then
[
"--privacy"
]
else
[ ]
) ++ (
map (result: "--result ${result}") feedConfig.mlat.results
);
in
{
enable = feedConfig.mlat.enable;
description = "${name} MLAT client.";
wants = [ "network.target" ];
after = [ "network.target" ];
unitConfig = {
Documentation = "https://github.com/makrsmark/mlat-client/";
};
serviceConfig = {
Type = "simple";
User = "${name}";
RuntimeDirectory = "${name}-mlat";
RuntimeDirectoryMode = "0755";
ExecStart = "${self.packages.${pkgs.system}.adsbfi-mlat-client}/bin/mlat-client ${lib.concatStringsSep " " options}";
SyslogIdentifier = "${name}-mlat";
Restart = "on-failure";
RestartSec = 30;
Nice = -1;
};
wantedBy = [ "multi-user.target" ];
}
);
createFeedService = (name: feedConfig:
let
uuidFile = pkgs.writeTextFile {
name = "${name}-uuid";
text = "${feedConfig.uuid}";
};
connectors = map
(
connector: "--net-connector ${connector}"
)
(
map
(
connector: (
lib.concatStringsSep "," (
[
connector.primary.host
(toString connector.primary.port)
connector.protocol
] ++ (
if connector.silentFail
then
[
"silent_fail"
]
else
[ ]
) ++ (
if connector.secondary != null
then
[
connector.secondary.host
(toString connector.secondary.port)
]
else
[ ]
)
)
)
)
feedConfig.feed.connectors
);
options = [
"--quiet"
"--net"
"--net-only"
"--write-json"
"%t/${name}-feed"
"--net-beast-reduce-interval 0.5"
"--net-heartbeat 60"
"--net-ro-size 1280"
"--net-ro-interval 0.2"
"--net-ro-port 0"
"--net-sbs-port 0"
"--net-bi-port ${toString feedConfig.feed.beastInputPort}"
"--net-bo-port 0"
"--net-ri-port 0"
"--write-json-every 1"
"--lat"
cfg.latitude
"--lon"
cfg.longitude
"--max-range 450"
"--json-location-accuracy 2"
"--range-outline-hours 24"
"--uuid-file"
"${uuidFile}"
] ++ connectors;
in
{
enable = feedConfig.feed.enable;
description = "ADSB.fi feeder.";
wants = [ "network.target" ];
after = [ "network.target" ];
unitConfig = {
Documentation = "https://github.com/widehopf/readsb/";
StartLimitIntervalSec = 1;
};
serviceConfig = {
Type = "simple";
User = "${name}";
RuntimeDirectory = "${name}-feed";
RuntimeDirectoryMode = "0755";
ExecStart = "${self.packages.${pkgs.system}.adsbfi-readsb}/bin/readsb ${lib.concatStringsSep " " options}";
SyslogIdentifier = "${name}-feed";
Restart = "on-failure";
RestartSec = 30;
StartLimitBurst = 100;
Nice = -1;
};
wantedBy = [ "multi-user.target" ];
}
);
in
lib.mkIf cfg.enable {
assertions = [
{
assertion = cfg.dump1090.device.type == "rtlsdr";
message = "not set up for anything but rtlsdr yet";
}
{
assertion = !(cfg.dump1090.device.type == "rtlsdr" && cfg.dump1090.device.rtlsdr.serial != null && cfg.dump1090.device.rtlsdr.index != null);
message = "rtlsdr device cannot be selected by both serial and index";
}
{
assertion = !(!cfg.dump978.enable && cfg.skyaware978.enable);
message = "The skyaware978 service cannot be enabled if the dump978 service is disabled.";
}
];
boot.blacklistedKernelModules = [
"dvb_usb_rtl28xxu"
];
services.udev.packages = [
pkgs.rtl-sdr
];
users.groups.plugdev = { };
users.groups.dump1090 = { };
users.users.dump1090 = {
isSystemUser = true;
group = "dump1090";
extraGroups = [ "plugdev" ];
};
users.groups.piaware = { };
users.users.piaware = {
isSystemUser = true;
group = "piaware";
};
systemd.services."dump1090-fa" =
let
rtlsdrOptions =
if cfg.dump1090.device.type == "rtlsdr"
then
(
if cfg.dump1090.device.rtlsdr.serial != null
then
[
"--device-index"
cfg.dump1090.device.rtlsdr.serial
]
else
[ ]
) ++
(
if cfg.dump1090.device.rtlsdr.index != null
then
[
"--device-index"
(toString cfg.dump1090.device.rtlsdr.index)
]
else
[ ]
) ++ (
if cfg.dump1090.device.rtlsdr.enableAgc
then
[
"--enable-agc"
]
else
[ ]
) ++ (
if cfg.dump1090.device.rtlsdr.frequencyCorrection != null
then
[
"--ppm"
(toString cfg.dump1090.device.rtlsdr.frequencyCorrection)
]
else
[ ]
) ++ (
if cfg.dump1090.device.rtlsdr.directSamplingMode != null
then
[
"--direct"
(toString cfg.dump1090.device.rtlsdr.directSamplingMode)
]
else
[ ]
)
else
[ ];
adaptiveDynamicRangeOptions =
if cfg.dump1090.adaptiveDynamicRange.enable
then
[
"--adaptive-range"
] ++
(
if cfg.dump1090.adaptiveDynamicRange.target != null
then
[ "--adaptive-range-target" (toString cfg.dump1090.adaptiveDynamicRange.target) ]
else
[ ]
) ++
(
if cfg.dump1090.adaptiveDynamicRange.alpha != null
then
[ "--adaptive-range-alpha" (toString cfg.dump1090.adaptiveDynamicRange.alpha) ]
else
[ ]
) ++
(
if cfg.dump1090.adaptiveDynamicRange.percentile != null
then
[ "--adaptive-range-percentile" (toString cfg.dump1090.adaptiveDynamicRange.percentile) ]
else
[ ]
) ++
(
if cfg.dump1090.adaptiveDynamicRange.changeDelay != null
then
[ "--adaptive-range-change-delay" (toString cfg.dump1090.adaptiveDynamicRange.changeDelay) ]
else
[ ]
) ++
(
if cfg.dump1090.adaptiveDynamicRange.scanDelay != null
then
[ "--adaptive-range-scan-delay" (toString cfg.dump1090.adaptiveDynamicRange.scanDelay) ]
else
[ ]
) ++
(
if cfg.dump1090.adaptiveDynamicRange.rescanDelay != null
then
[ "--adaptive-range-rescan-delay" (toString cfg.dump1090.adaptiveDynamicRange.rescanDelay) ]
else
[ ]
)
else
[ ];
options = [
"--quiet"
"--device-type"
cfg.dump1090.device.type
] ++ rtlsdrOptions ++ adaptiveDynamicRangeOptions ++ (
if cfg.dump1090.device.gain != null
then
[
"--gain"
(toString cfg.dump1090.device.gain)
]
else
[ ]
) ++ (
if cfg.dump1090.errorCorrection
then
[ "--fix" ]
else
[ ]
) ++ [
"--lat"
cfg.latitude
"--lon"
cfg.longitude
] ++
(
if cfg.dump1090.maxRange != null
then
[
"--max-range"
(toString cfg.dump1090.maxRange)
]
else
[ ]
) ++ (
if cfg.dump1090.network.raw.input.enable
then
[
"--net-ri-port"
(lib.concatStringsSep "," (map toString cfg.dump1090.network.raw.input.ports))
]
else
[ ]
) ++ (
if cfg.dump1090.network.raw.output.enable
then
[
"--net-ro-port"
(lib.concatStringsSep "," (map toString cfg.dump1090.network.raw.output.ports))
]
else
[ ]
) ++ (
if cfg.dump1090.network.raw.output.size != null
then
[
"--net-ro-size"
(toString cfg.dump1090.network.raw.output.size)
]
else
[ ]
) ++ (
if cfg.dump1090.network.raw.output.interval != null
then
[
"--net-ro-interval"
(toString cfg.dump1090.network.raw.output.interval)
]
else
[ ]
) ++
(
if cfg.dump1090.network.baseStation.enable
then
[
"--net-sbs-port"
(lib.concatStringsSep "," (map toString cfg.dump1090.network.baseStation.ports))
]
else
[ ]
) ++
(
if cfg.dump1090.network.beast.input.enable
then
[
"--net-bi-port"
(lib.concatStringsSep "," (map toString cfg.dump1090.network.beast.input.ports))
]
else [ ]
) ++
(
if cfg.dump1090.network.beast.output.enable
then
[
"--net-bo-port"
(lib.concatStringsSep "," (map toString cfg.dump1090.network.beast.output.ports))
]
else [ ]
) ++ [
"--json-location-accuracy"
(toString cfg.dump1090.jsonLocationAccuracy)
"--write-json"
"%t/dump1090-fa"
] ++ cfg.dump1090.extraOptions;
in
{
enable = cfg.dump1090.enable;
description = "dump1090 ADS-B receiver (FlightAware customization)";
wants = [ "network.target" ];
after = [ "network.target" ];
unitConfig = {
Documentation = "https://flightaware.com/adsb/piaware/";
};
serviceConfig = {
Type = "simple";
User = "dump1090";
RuntimeDirectory = "dump1090-fa";
RuntimeDirectoryMode = "0755";
ExecStart = "${self.packages.${pkgs.system}.dump1090-fa}/bin/dump1090 ${lib.concatStringsSep " " options}";
SyslogIdentifier = "dump1090-fa";
Restart = "on-failure";
RestartSec = 30;
RestartPreventExitStatus = "64";
Nice = -5;
};
wantedBy = [ "multi-user.target" ];
};
users.groups.skyaware = { };
users.users.skyaware = {
isSystemUser = true;
group = "skyaware";
};
systemd.services."skyaware978" = {
enable = cfg.skyaware978.enable;
description = "skyaware978 ADS-B UAT web display";
wants = [ "network.target" ];
after = [ "network.target" "dump978-fa.service" ];
unitConfig = {
Documentation = "https://flightaware.com/adsb/piaware/";
};
serviceConfig = {
Type = "simple";
User = "skyaware";
RuntimeDirectory = "skyaware978";
RuntimeDirectoryMode = "0755";
ExecStart = "${self.packages.${pkgs.system}.dump978-fa}/bin/skyaware978 --lat ${cfg.latitude} --lon ${cfg.longitude} --json-dir %t/skyaware978";
SyslogIdentifier = "skyaware978";
Restart = "on-failure";
RestartSec = 30;
RestartPreventExitStatus = "64";
};
wantedBy = [ "dump978-fa.service" ];
};
users.groups.adsbexchange = { };
users.users.adsbexchange = {
isSystemUser = true;
group = "adsbexchange";
};
systemd.services."adsbexchange-mlat" =
let
uuidFile = pkgs.writeTextFile {
name = "adsbx-uuid";
text = "${cfg.adsbexchange.uuid}";
};
options = [
"--input-type"
cfg.adsbexchange.mlat.input.type
"--no-udp"
"--input-connect"
"${cfg.adsbexchange.mlat.input.host}:${toString cfg.adsbexchange.mlat.input.port}"
"--server"
cfg.adsbexchange.mlat.server
"--user"
cfg.adsbexchange.username
"--lat"
cfg.latitude
"--lon"
cfg.longitude
"--alt"
(toString cfg.altitude)
"--uuid-file"
"${uuidFile}"
] ++ (
if cfg.adsbexchange.mlat.privacy
then
[
"--privacy"
]
else
[ ]
) ++ (
map (result: "--result ${result}") cfg.adsbexchange.mlat.results
);
in
{
enable = cfg.adsbexchange.mlat.enable;
description = "ADS-B Exchange MLAT client.";
wants = [ "network.target" ];
after = [ "network.target" ];
unitConfig = {
Documentation = "https://github.com/makrsmark/mlat-client/";
};
serviceConfig = {
Type = "simple";
User = "adsbexchange";
RuntimeDirectory = "adsbexchange-mlat";
RuntimeDirectoryMode = "0755";
ExecStart = "${self.packages.${pkgs.system}.adsbfi-mlat-client}/bin/mlat-client ${lib.concatStringsSep " " options}";
SyslogIdentifier = "adsbexchange-mlat";
Restart = "on-failure";
RestartSec = 30;
Nice = -1;
};
wantedBy = [ "multi-user.target" ];
};
systemd.services."adsbexchange-feed" =
let
uuidFile = pkgs.writeTextFile {
name = "adsbx-uuid";
text = "${cfg.adsbexchange.uuid}";
};
options = [
"--quiet"
"--net"
"--net-only"
"--write-json"
"%t/adsbexchange-feed"
"--net-beast-reduce-interval 0.5"
"--net-connector feed1.adsbexchange.com,30004,beast_reduce_out,feed2.adsbexchange.com,64004"
"--net-heartbeat 60"
"--net-ro-size 1280"
"--net-ro-interval 0.2"
"--net-ro-port 0"
"--net-sbs-port 0"
"--net-bi-port ${toString cfg.adsbexchange.feed.beastInputPort}"
"--net-bo-port 0"
"--net-ri-port 0"
"--write-json-every 1"
"--lat"
cfg.latitude
"--lon"
cfg.longitude
"--max-range 450"
"--json-location-accuracy 2"
"--range-outline-hours 24"
"--uuid-file"
"${uuidFile}"
"--net-connector 127.0.0.1,30978,uat_in,silent_fail"
"--net-connector 127.0.0.1,30005,beast_in,silent_fail"
];
in
{
enable = cfg.adsbexchange.feed.enable;
description = "ADS-B Exchange feeder.";
wants = [ "network.target" ];
after = [ "network.target" ];
unitConfig = {
Documentation = "https://github.com/wiedehopf/readsb/";
StartLimitIntervalSec = 1;
};
serviceConfig = {
Type = "simple";
User = "adsbexchange";
RuntimeDirectory = "adsbexchange-feed";
RuntimeDirectoryMode = "0755";
ExecStart = "${self.packages.${pkgs.system}.adsbfi-readsb}/bin/readsb ${lib.concatStringsSep " " options}";
SyslogIdentifier = "adsbexchange-feed";
Restart = "on-failure";
RestartSec = 30;
StartLimitBurst = 100;
Nice = -1;
};
wantedBy = [ "multi-user.target" ];
};
##
## ADSB.fi
##
users.groups.adsbfi = { };
users.users.adsbfi = {
isSystemUser = true;
group = "adsbexchange";
};
systemd.services."adsbfi-mlat" =
let
uuidFile = pkgs.writeTextFile {
name = "adsbfi-uuid";
text = "${cfg.adsbfi.uuid}";
};
options = [
"--input-type"
cfg.adsbfi.mlat.input.type
"--no-udp"
"--input-connect"
"${cfg.adsbfi.mlat.input.host}:${toString cfg.adsbfi.mlat.input.port}"
"--server"
cfg.adsbfi.mlat.server
"--user"
cfg.adsbfi.username
"--lat"
cfg.latitude
"--lon"
cfg.longitude
"--alt"
(toString cfg.altitude)
"--uuid-file"
"${uuidFile}"
] ++ (
if cfg.adsbfi.mlat.privacy
then
[
"--privacy"
]
else
[ ]
) ++ (
map (result: "--result ${result}") cfg.adsbfi.mlat.results
);
in
{
enable = cfg.adsbfi.mlat.enable;
description = "ADSB.fi MLAT client.";
wants = [ "network.target" ];
after = [ "network.target" ];
unitConfig = {
Documentation = "https://github.com/makrsmark/mlat-client/";
};
serviceConfig = {
Type = "simple";
User = "adsbfi";
RuntimeDirectory = "adsbfi-mlat";
RuntimeDirectoryMode = "0755";
ExecStart = "${self.packages.${pkgs.system}.adsbfi-mlat-client}/bin/mlat-client ${lib.concatStringsSep " " options}";
SyslogIdentifier = "adsbfi-mlat";
Restart = "on-failure";
RestartSec = 30;
Nice = -1;
};
wantedBy = [ "multi-user.target" ];
};
systemd.services."adsbfi-feed" =
let
uuidFile = pkgs.writeTextFile {
name = "adsbfi-uuid";
text = "${cfg.adsbfi.uuid}";
};
connectors = map
(
connector: "--net-connector ${connector}"
)
(
map
(
connector: (
lib.concatStringsSep "," (
[
connector.primary.host
(toString connector.primary.port)
connector.protocol
] ++ (
if connector.silentFail
then
[
"silent_fail"
]
else
[ ]
) ++ (
if connector.secondary != null
then
[
connector.secondary.host
(toString connector.secondary.port)
]
else
[ ]
)
)
)
)
cfg.adsbfi.feed.connectors
);
options = [
"--quiet"
"--net"
"--net-only"
"--write-json"
"%t/adsbfi-feed"
"--net-beast-reduce-interval 0.5"
"--net-heartbeat 60"
"--net-ro-size 1280"
"--net-ro-interval 0.2"
"--net-ro-port 0"
"--net-sbs-port 0"
"--net-bi-port ${toString cfg.adsbfi.feed.beastInputPort}"
"--net-bo-port 0"
"--net-ri-port 0"
"--write-json-every 1"
"--lat"
cfg.latitude
"--lon"
cfg.longitude
"--max-range 450"
"--json-location-accuracy 2"
"--range-outline-hours 24"
"--uuid-file"
"${uuidFile}"
] ++ connectors;
in
{
enable = cfg.adsbfi.feed.enable;
description = "ADSB.fi feeder.";
wants = [ "network.target" ];
after = [ "network.target" ];
unitConfig = {
Documentation = "https://github.com/widehopf/readsb/";
StartLimitIntervalSec = 1;
};
serviceConfig = {
Type = "simple";
User = "adsbfi";
RuntimeDirectory = "adsbfi-feed";
RuntimeDirectoryMode = "0755";
ExecStart = "${self.packages.${pkgs.system}.adsbfi-readsb}/bin/readsb ${lib.concatStringsSep " " options}";
SyslogIdentifier = "adsbfi-feed";
Restart = "on-failure";
RestartSec = 30;
StartLimitBurst = 100;
Nice = -1;
};
wantedBy = [ "multi-user.target" ];
};
##
## theairtraffic.com
##
users.groups.theairtraffic = { };
users.users.theairtraffic = {
isSystemUser = true;
group = "theairtraffic";
};
systemd.services."theairtraffic-mlat" = createMlatService "theairtraffic" cfg.theairtraffic;
systemd.services."theairtraffic-feed" = createFeedService "theairtraffic" cfg.theairtraffic;
systemd.tmpfiles.rules = [
"d /var/cache/piaware 0755 piaware piaware - -"
];
systemd.services."piaware" =
let
config = pkgs.writeTextFile {
name = "piaware.conf";
text = ''
# https://flightaware.com/adsb/piaware/advanced_configuration
allow-auto-updates no
allow-manual-updates no
allow-mlat ${if cfg.piaware.allowMLAT then "yes" else "no"}
allow-modeac ${if cfg.piaware.allowModeAC then "yes" else "no"}
feeder-id ${cfg.piaware.feederId}
'';
};
in
{
enable = cfg.piaware.enable;
description = "FlightAware ADS-B uploader";
wants = [ "network-online.target" ];
after = [ "dump1090-fa.service" "network-online.target" "time-sync.target" ];
unitConfig = {
Documentation = "https://flightaware.com/adsb/piaware/";
};
serviceConfig = {
Type = "simple";
User = "piaware";
RuntimeDirectory = "piaware";
ExecStart = "${self.packages.${pkgs.system}.piaware}/bin/piaware -p %t/piaware/piaware.pid -plainlog -configfile ${config} -statusfile %t/piaware/status.json";
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
Restart = "on-failure";
RestartPreventExitStatus = "4 6";
};
wantedBy = [ "multi-user.target" ];
reloadTriggers = [
"/etc/piaware.conf"
];
};
services.nginx = {
enable = true;
virtualHosts = {
"_" = {
root = self.packages.${pkgs.system}.dump1090-fa.html;
locations = {
"=/status.json" = {
alias = "/run/piaware/status.json";
extraConfig = ''
add_header Access-Control-Allow-Origin *;
'';
};
"/data/" = {
alias = "/run/dump1090-fa/";
extraConfig = ''
add_header Access-Control-Allow-Origin *;
'';
};
"/data-978/" = {
root = "/run/skyaware978/";
extraConfig = ''
add_header Access-Control-Allow-Origin *;
'';
};
};
};
};
};
}

12
modules/default.nix Normal file
View file

@ -0,0 +1,12 @@
{ self, ... }: {
nixosModules = {
adsb = { config, lib, pkgs, ... }:
let
cfg = config.adsb;
in
{
options = import ./options { inherit self cfg lib; };
config = import ./config { inherit self cfg lib pkgs; };
};
};
}

View file

@ -0,0 +1,96 @@
{ cfg, lib, netConnector, UUID }:
lib.options.mkOption {
type = lib.types.submodule {
options = {
username = lib.options.mkOption {
type = lib.types.nullOr (lib.types.addCheck lib.types.str (
username: (builtins.match ''^[a-zA-Z0-9_-]+$'' username) != null
));
default = null;
example = "'william34-london' or 'william34-jersey'";
description = "A unique name to be shown on the ADS-B Exchange MLAT map (map.adsbexchange.com/mlat-map)";
};
uuid = lib.options.mkOption {
type = UUID;
};
mlat = lib.options.mkOption {
type = lib.types.submodule {
options = {
enable = lib.options.mkOption {
type = lib.types.bool;
default = false;
description = "Enable ADS-B Exchange MLAT client.";
};
input = lib.options.mkOption {
type = lib.types.submodule {
options = {
host = lib.options.mkOption {
type = lib.types.str;
default = "127.0.0.1";
example = "127.0.0.1";
description = "";
};
port = lib.options.mkOption {
type = lib.types.port;
default = 30005;
example = 30005;
description = "";
};
type = lib.options.mkOption {
type = lib.types.enum [ "dump1090" "radarcape_gps" ];
default = "dump1090";
example = "dump1090";
description = "";
};
};
};
default = { };
};
reduceInterval = lib.options.mkOption {
type = lib.types.float;
default = 0.5;
example = "0.5";
description = "";
};
results = lib.options.mkOption {
type = lib.types.listOf lib.types.str;
default = [
"beast,connect,127.0.0.1:${toString cfg.adsbexchange.feed.beastInputPort}"
];
description = "Where to send results of MLAT computations.";
};
privacy = lib.options.mkOption {
type = lib.types.bool;
default = false;
description = "Sets the privacy flag for this receiver. Currently, this removes the receiver location pin from the coverage maps.";
};
server = lib.options.mkOption {
type = lib.types.str;
default = "feed.adsbexchange.com:31090";
example = "feed.adsbexchange.com:31090";
description = "Server to feed MLAT data to";
};
};
};
default = { };
};
feed = lib.options.mkOption {
type = lib.types.submodule {
options = {
enable = lib.options.mkOption {
type = lib.types.bool;
default = false;
description = "Enable ADS-B Exchange feeder.";
};
beastInputPort = lib.options.mkOption {
type = lib.types.port;
default = 30154;
description = "Port to accept Beast messages on.";
};
};
};
};
};
};
default = { };
}

144
modules/options/adsbfi.nix Normal file
View file

@ -0,0 +1,144 @@
##
## ADSB.fi configuration options
##
{ cfg, lib, netConnector, UUID }:
lib.options.mkOption {
type = lib.types.submodule {
options = {
username = lib.options.mkOption {
type = lib.types.nullOr (lib.types.addCheck lib.types.str (
username: (builtins.match ''^[a-zA-Z0-9_-]+$'' username) != null
));
default = null;
example = "'william34-london' or 'william34-jersey'";
description = "A unique name to be shown on the ADSB.fi MLAT map";
};
uuid = lib.options.mkOption {
type = UUID;
};
mlat = lib.options.mkOption {
type = lib.types.submodule {
options = {
enable = lib.options.mkOption {
type = lib.types.bool;
default = false;
description = "Enable ADSD.fi MLAT client.";
};
input = lib.options.mkOption {
type = lib.types.submodule {
options = {
host = lib.options.mkOption {
type = lib.types.str;
default = "127.0.0.1";
example = "127.0.0.1";
description = "";
};
port = lib.options.mkOption {
type = lib.types.port;
default = 30005;
example = 30005;
description = "";
};
type = lib.options.mkOption {
type = lib.types.enum [
"dump1090"
"radarcape_gps"
];
default = "dump1090";
example = "dump1090";
description = "";
};
};
};
default = { };
};
reduceInterval = lib.options.mkOption {
type = lib.types.float;
default = 0.5;
example = "0.5";
description = "";
};
results = lib.options.mkOption {
type = lib.types.listOf lib.types.str;
default = [
"beast,connect,127.0.0.1:${toString cfg.adsbfi.feed.beastInputPort}"
];
description = "";
};
privacy = lib.options.mkOption {
type = lib.types.bool;
default = false;
description = "Sets the privacy flag for this receiver. Currently, this removes the receiver location pin from the coverage maps.";
};
server = lib.options.mkOption {
type = lib.types.str;
default = "feed.adsb.fi:31090";
example = "feed.adsb.fi:31090";
description = "Server to feed MLAT data to";
};
};
};
};
feed = lib.options.mkOption {
type = lib.types.submodule {
options = {
enable = lib.options.mkOption {
type = lib.types.bool;
default = false;
description = "Enable ADSB.fi feeder.";
};
beastInputPort = lib.options.mkOption {
type = lib.types.port;
default = 30155;
description = "Port to accept Beast messages on.";
};
connectors = lib.options.mkOption {
type = lib.types.listOf netConnector;
default = [
{
protocol = "uat_in";
primary = {
host = "127.0.0.1";
port = 30978;
};
silentFail = true;
}
{
protocol = "beast_in";
primary = {
host = "127.0.0.1";
port = 30005;
};
silentFail = true;
}
{
protocol = "beast_reduce_out";
primary = {
host = "feed.adsb.fi";
port = 30004;
};
secondary = {
host = "feed.adsb.fi";
port = 64004;
};
}
];
};
};
};
};
};
};
}

View file

@ -0,0 +1,95 @@
{ cfg, lib, ... }:
let
hostPort = lib.types.submodule {
options = {
host = lib.options.mkOption {
type = lib.types.str;
};
port = lib.options.mkOption {
type = lib.types.port;
};
};
};
netConnector = lib.types.submodule {
options = {
protocol = lib.options.mkOption {
type = lib.types.enum [
"beast_in"
"beast_out"
"beast_reduce_out"
"beast_reduce_plus_out"
"gpsd_in"
"json_out"
"raw_in"
"raw_out"
"sbs_in"
"sbs_in_jaero"
"sbs_in_mlat"
"sbs_out"
"sbs_out_jaero"
"sbs_out_mlat"
"sbs_out_replay"
"uat_in"
"vrs_out"
];
};
primary = lib.options.mkOption {
type = hostPort;
};
silentFail = lib.options.mkOption {
type = lib.types.bool;
default = false;
};
secondary = lib.options.mkOption {
type = lib.types.nullOr hostPort;
default = null;
};
};
};
UUID = lib.types.addCheck lib.types.str (
uuid: (builtins.match ''^[A-F0-9a-f]{8}-[A-F0-9a-f]{4}-[A-F0-9a-f]{4}-[A-F0-9a-f]{4}-[A-F0-9a-f]{12}$'' uuid) != null
);
in
{
adsb = lib.options.mkOption {
type = lib.types.submodule {
options = {
enable = lib.options.mkEnableOption "ADS-B";
latitude = lib.options.mkOption {
type = lib.types.addCheck lib.types.str (
lat:
(builtins.match ''^([+-])?[0-9]+(\.[0-9]{5,})?$'' lat) != null && (builtins.fromJSON lat) >= -90.0 && (builtins.fromJSON lat) <= 90.0
);
description = "Latitude of the receiver expressed in degrees with at least 5 decimal places. For MLAT the precise location of your antenna is required. A small error of 15m/45ft will cause issues with MLAT!";
};
longitude = lib.options.mkOption {
type = lib.types.addCheck lib.types.str (
lon:
(builtins.match ''^([+-])?[0-9]+(\.[0-9]{5,})?$'' lon) != null && (builtins.fromJSON lon) >= -180.0 && (builtins.fromJSON lon) <= 180.0
);
description = "Longitude of the receiver expressed in degrees with at least 5 decimal places. For MLAT the precise location of your antenna is required. A small error of 15m/45ft will cause issues with MLAT!";
};
altitude = lib.options.mkOption {
type = lib.types.float;
description = "The altitude if the receiver above sea level (in meters).";
};
dump1090 = import ./dump1090.nix { inherit lib; };
dump978 = import ./dump978.nix { inherit lib; };
skyaware978 = import ./skyaware978.nix { inherit lib; };
adsbexchange = import ./adsbexchange.nix { inherit cfg lib netConnector UUID; };
adsbfi = import ./adsbfi.nix { inherit cfg lib netConnector UUID; };
piaware = import ./piaware.nix { inherit lib UUID; };
theairtraffic = import ./theairtraffic.nix { inherit cfg lib netConnector UUID; };
};
};
};
}

View file

@ -0,0 +1,293 @@
{ lib }:
lib.options.mkOption {
type = lib.types.submodule {
options = {
enable = lib.options.mkEnableOption "Dump1090";
errorCorrection = lib.options.mkOption {
type = lib.types.bool;
default = true;
};
maxRange = lib.options.mkOption {
type = lib.types.addCheck lib.types.int (x: x >= 0);
default = 360;
description = "Absolute maximum range for position decoding (in NM)";
};
jsonLocationAccuracy = lib.options.mkOption {
type = lib.types.enum [ 0 1 2 ];
default = 2;
description = "Accuracy of receiver location in json metadata (0=no location, 1=approximate, 2=exact)";
};
adaptiveDynamicRange = lib.options.mkOption {
type = lib.types.submodule {
options = {
enable = lib.options.mkOption {
type = lib.types.bool;
default = true;
description = "Adjust gain for target dynamic range";
};
target = lib.options.mkOption {
type = lib.types.nullOr lib.types.int;
default = null;
description = "Set target dynamic range in dB";
};
alpha = lib.options.mkOption {
type = lib.types.nullOr (lib.types.addCheck lib.types.float (alpha: alpha >= 0.0 && alpha <= 1.0));
default = null;
description = "Set dynamic range noise smoothing factor (0..1, smaller=more smoothing)";
};
percentile = lib.options.mkOption {
type = lib.types.nullOr (lib.types.addCheck lib.types.int (x: x >= 0 && x <= 100));
default = null;
description = "Set dynamic range noise percentile";
};
changeDelay = lib.options.mkOption {
type = lib.types.nullOr (lib.types.addCheck lib.types.int (x: x >= 0));
default = null;
description = "Set delay after changing gain before resuming dynamic range control (seconds)";
};
scanDelay = lib.options.mkOption {
type = lib.types.nullOr (lib.types.addCheck lib.types.int (x: x >= 0));
default = null;
description = "Set scan interval for dynamic range gain scanning following a gain decrease due to an increase in noise (seconds)";
};
rescanDelay = lib.options.mkOption {
type = lib.types.nullOr (lib.types.addCheck lib.types.int (x: x >= 0));
default = null;
description = "Set periodic rescan interval for dynamic range gain scanning (seconds)";
};
};
};
default = { };
description = "Configuration options for adaptive dynamic range.";
};
device = lib.options.mkOption {
type = lib.types.submodule {
options = {
type = lib.options.mkOption {
type = lib.types.enum [ "rtlsdr" "bladerf" "hackrf" "limesdr" ];
default = "rtlsdr";
};
rtlsdr = lib.options.mkOption {
type = lib.types.submodule {
options = {
serial = lib.options.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "select device by serial";
};
index = lib.options.mkOption {
type = lib.types.nullOr lib.types.int;
default = null;
description = "select device by index";
};
enableAgc = lib.options.mkOption {
type = lib.types.bool;
default = false;
description = "enable digital AGC (not tuner AGC!)";
};
frequencyCorrection = lib.options.mkOption {
type = lib.types.nullOr lib.types.int;
default = null;
description = "set oscillator frequency correction in PPM";
};
directSamplingMode = lib.options.mkOption {
type = lib.types.nullOr (lib.types.enum [ 0 1 2 ]);
default = null;
description = "set direct sampling mode";
};
};
};
};
gain = lib.options.mkOption {
type = lib.types.nullOr lib.types.int;
default = null;
description = "Set gain in dB (default: varies by SDR type)";
};
frequency = lib.options.mkOption {
type = lib.types.nullOr lib.types.int;
default = null;
description = "Set frequency (default: 1090 Mhz)";
};
};
};
};
network = lib.options.mkOption {
type = lib.types.submodule {
options = {
raw = lib.options.mkOption {
type = lib.types.submodule {
options = {
input = lib.options.mkOption {
type = lib.types.submodule {
options = {
enable = lib.options.mkOption {
type = lib.types.bool;
default = false;
description = "Enable TCP raw input";
};
ports = lib.options.mkOption {
type = lib.types.listOf lib.types.port;
default = [ 30001 ];
description = "TCP raw input listen ports (default: 30001)";
};
};
};
default = { };
};
output = lib.options.mkOption {
type = lib.types.submodule {
options = {
enable = lib.options.mkOption {
type = lib.types.bool;
default = true;
description = "Enable TCP raw output";
};
ports = lib.options.mkOption {
type = lib.types.listOf lib.types.port;
default = [ 30002 ];
description = "TCP raw output listen ports (default: 30002)";
};
size = lib.options.mkOption {
type = lib.types.nullOr (lib.types.addCheck lib.types.int (x: x >= 0));
default = null;
description = "TCP output minimum size (default: 0)";
};
interval = lib.options.mkOption {
type = lib.types.nullOr (lib.types.addCheck lib.types.int (x: x >= 0));
default = null;
description = "TCP output memory flush rate in seconds (default: 0)";
};
};
};
default = { };
};
};
};
default = { };
};
baseStation = lib.options.mkOption {
type = lib.types.submodule {
options = {
enable = lib.options.mkOption {
type = lib.types.bool;
default = true;
};
ports = lib.options.mkOption {
type = lib.types.listOf lib.types.port;
default = [ 30003 ];
};
};
};
default = { };
};
beast = lib.options.mkOption {
type = lib.types.submodule {
options = {
input = lib.options.mkOption {
type = lib.types.submodule {
options = {
enable = lib.options.mkOption {
type = lib.types.bool;
default = true;
};
ports = lib.options.mkOption {
type = lib.types.listOf lib.types.port;
default = [ 30004 30104 ];
};
};
};
default = { };
};
output = lib.options.mkOption {
type = lib.types.submodule {
options = {
enable = lib.options.mkOption {
type = lib.types.bool;
default = true;
};
ports = lib.options.mkOption {
type = lib.types.listOf lib.types.port;
default = [ 30005 ];
};
};
};
default = { };
};
stratux = lib.options.mkOption {
type = lib.types.submodule {
options = {
enable = lib.options.mkOption {
type = lib.types.bool;
default = false;
};
ports = lib.options.mkOption {
type = lib.types.listOf lib.types.port;
default = [ ];
};
};
};
default = { };
};
};
};
default = { };
};
};
};
default = { };
};
extraOptions = lib.options.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
};
};
};
}

View file

@ -0,0 +1,15 @@
{ lib }:
lib.options.mkOption {
type = lib.types.submodule {
options = {
enable = lib.options.mkOption {
type = lib.types.bool;
default = false;
description = "Enable the dump978 service.";
};
};
};
default = { };
}

View file

@ -0,0 +1,26 @@
{ lib, UUID }:
lib.options.mkOption {
type = lib.types.submodule {
options = {
enable = lib.options.mkEnableOption "PiAware";
feederId = lib.options.mkOption {
type = lib.types.nullOr UUID;
default = null;
description = "PiAware feeder ID";
};
allowMLAT = lib.options.mkOption {
type = lib.types.bool;
default = true;
description = "Should PiAware enable multilateration where possible? You may need to disable this if multilateration overloads your receiver";
};
allowModeAC = lib.options.mkOption {
type = lib.types.bool;
default = true;
description = "Should PiAware enable reception of Mode A/C messages when requested? You may need to disable this if processing Mode A/C overloads your receiver.";
};
};
};
}

View file

@ -0,0 +1,15 @@
{ lib }:
lib.options.mkOption {
type = lib.types.submodule {
options = {
enable = lib.options.mkOption {
type = lib.types.bool;
default = false;
description = "Enable the skyaware978 service.";
};
};
};
default = { };
}

View file

@ -0,0 +1,156 @@
##
## theairtraffic.com
##
{ cfg, lib, netConnector, UUID }: lib.options.mkOption {
type = lib.types.submodule {
options = {
name = lib.options.mkOption {
type = lib.types.enum [
"adsbexchange"
"adsbfi"
"theairtraffic"
];
};
username = lib.options.mkOption {
type = lib.types.nullOr (lib.types.addCheck lib.types.str (
username: (builtins.match ''^[a-zA-Z0-9_-]+$'' username) != null
));
default = null;
example = "'william34-london' or 'william34-jersey'";
description = "A unique name to be shown on the MLAT map";
};
uuid = lib.options.mkOption {
type = UUID;
};
mlat = lib.options.mkOption {
type = lib.types.submodule {
options = {
enable = lib.options.mkOption {
type = lib.types.bool;
default = false;
description = "Enable MLAT client.";
};
input = lib.options.mkOption {
type = lib.types.submodule {
options = {
host = lib.options.mkOption {
type = lib.types.str;
default = "127.0.0.1";
example = "127.0.0.1";
description = "";
};
port = lib.options.mkOption {
type = lib.types.port;
default = 30005;
example = 30005;
description = "";
};
type = lib.options.mkOption {
type = lib.types.enum [
"dump1090"
"radarcape_gps"
];
default = "dump1090";
example = "dump1090";
description = "";
};
};
};
default = { };
};
reduceInterval = lib.options.mkOption {
type = lib.types.float;
default = 0.5;
example = "0.5";
description = "";
};
results = lib.options.mkOption {
type = lib.types.listOf lib.types.str;
default = [
"beast,connect,127.0.0.1:${toString cfg.theairtraffic.feed.beastInputPort}"
];
description = "";
};
privacy = lib.options.mkOption {
type = lib.types.bool;
default = false;
description = ''
Sets the privacy flag for this receiver. Currently, this removes the receiver
location pin from the coverage maps.
'';
};
server = lib.options.mkOption {
type = lib.types.str;
default = "feed.theairtraffic.com:31090";
example = "feed.theairtraffic.com:31090";
description = "Server to feed MLAT data to";
};
};
};
};
feed = lib.options.mkOption {
type = lib.types.submodule {
options = {
enable = lib.options.mkOption {
type = lib.types.bool;
default = false;
description = "Enable theairtraffic.com feeder.";
};
beastInputPort = lib.options.mkOption {
type = lib.types.port;
default = 30156;
description = "Port to accept Beast messages on.";
};
connectors = lib.options.mkOption {
type = lib.types.listOf netConnector;
default = [
{
protocol = "uat_in";
primary = {
host = "127.0.0.1";
port = 30978;
};
silentFail = true;
}
{
protocol = "beast_in";
primary = {
host = "127.0.0.1";
port = 30005;
};
silentFail = true;
}
{
protocol = "beast_reduce_out";
primary = {
host = "feed.theairtraffic.com";
port = 30004;
};
}
];
};
};
};
};
};
};
}

View file

@ -0,0 +1,22 @@
{ pkgs, ... }:
let
pname = "adsbexchange-mlat-client";
version = "0.4.2";
in
pkgs.python3.pkgs.buildPythonApplication {
inherit pname version;
src = pkgs.fetchFromGitHub {
owner = "ADSBexchange";
repo = "mlat-client";
rev = "faf9638fe8c2eafc2abdc45621ff879c7acb882b";
sha256 = "sha256-V//LpYmBXtT8haX1aZ4XldzzyUY2YN7x3lTpQ2csTmw=";
};
meta = with pkgs.lib; {
homepage = "https://github.com/adsbexchange/mlat-client";
description = "ADS-B Exchange Mode S multilateration client";
longDescription = ''
This is a client that selectively forwards Mode S messages to a server that resolves the transmitter position by multilateration of the same message received by multiple clients.
'';
license = licenses.gpl3Plus;
};
}

View file

@ -0,0 +1,40 @@
{ pkgs, ... }:
let
pname = "adsbexchange-readsb";
version = "3.14.1597";
in
pkgs.stdenv.mkDerivation {
inherit pname version;
src = pkgs.fetchFromGitHub {
owner = "adsbexchange";
repo = "readsb";
rev = "61c6bb02ae1af04c805e2607bfec5dbfea4c6515";
sha256 = "sha256-LXIib8mfgFMINXCYHxfnhdGI5WjJsCNq0LV+i+Kp4r8=";
};
nativeBuildInputs = [
pkgs.pkg-config
];
propagatedBuildInputs = [
pkgs.libad9361
pkgs.libbladeRF
pkgs.libiio
pkgs.librtlsdr
pkgs.ncurses
pkgs.zlib
pkgs.zstd
];
buildFlags = [
"READSB_VERSION=v${version}"
"RTLSDR=yes"
"BLADERF=yes"
"PLUTOSDR=yes"
];
installPhase = ''
runHook preInstall
mkdir -p $out/bin
install -D readsb $out/bin
install -D viewadsb $out/bin
ln -s readsb $out/bin/feed-adsbx
runHook postInstall
'';
}

View file

@ -0,0 +1,22 @@
{ pkgs, ... }:
let
pname = "adsbfi-mlat-client";
version = "0.4.2";
in
pkgs.python3.pkgs.buildPythonApplication {
inherit pname version;
src = pkgs.fetchFromGitHub {
owner = "makrsmark";
repo = "mlat-client";
rev = "0e8f9f927339e6bfa8cadaa8fff6cb734de4b60a";
sha256 = "sha256-4dWwRBKWkxMjKJmZtAAZjaFqkGubOmKTmzmntcR92II=";
};
meta = with pkgs.lib; {
homepage = "https://github.com/makrsmark/mlat-client";
description = "ADSB.fi Mode S multilateration client";
longDescription = ''
This is a client that selectively forwards Mode S messages to a server that resolves the transmitter position by multilateration of the same message received by multiple clients.
'';
license = licenses.gpl3Plus;
};
}

View file

@ -0,0 +1,40 @@
{ pkgs, ... }:
let
pname = "adsbfi-readsb";
version = "3.14.1606";
in
pkgs.stdenv.mkDerivation {
inherit pname version;
src = pkgs.fetchFromGitHub {
owner = "wiedehopf";
repo = "readsb";
rev = "v${version}";
sha256 = "sha256-XjvWpxCYCoNPDPwbvLKk8l23dhHL98HbVK6+Zn+36Wk=";
};
nativeBuildInputs = [
pkgs.pkg-config
];
propagatedBuildInputs = [
pkgs.libad9361
pkgs.libbladeRF
pkgs.libiio
pkgs.librtlsdr
pkgs.ncurses
pkgs.zlib
pkgs.zstd
];
buildFlags = [
"READSB_VERSION=v${version}"
"RTLSDR=yes"
"BLADERF=yes"
"PLUTOSDR=yes"
"AIRCRAFT_HASH_BITS=12"
];
installPhase = ''
runHook preInstall
mkdir -p $out/bin
install -D readsb $out/bin
install -D viewadsb $out/bin
runHook postInstall
'';
}

28
packages/default.nix Normal file
View file

@ -0,0 +1,28 @@
{ self, flake-utils, nixpkgs }: flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = import nixpkgs {
inherit system;
};
in
{
packages = {
dump1090-fa = import ./dump1090-fa.nix { inherit pkgs; };
dump978-fa = import ./dump978-fa.nix { inherit pkgs; };
tcllauncher = import ./tcllauncher.nix { inherit pkgs; };
mutability-mlat-client = import ./mutability-mlat-client.nix { inherit pkgs; };
piaware = import ./piaware.nix {
inherit pkgs;
dump1090-fa = self.packages.${system}.dump1090-fa;
dump978-fa = self.packages.${system}.dump978-fa;
tcllauncher = self.packages.${system}.tcllauncher;
mutability-mlat-client = self.packages.${system}.mutability-mlat-client;
};
adsbexchange-mlat-client = import ./adsbexchange-mlat-client.nix { inherit pkgs; };
adsbexchange-readsb = import ./adsbexchange-readsb.nix { inherit pkgs; };
adsbfi-mlat-client = import ./adsbfi-mlat-client.nix { inherit pkgs; };
adsbfi-readsb = import ./adsbfi-readsb.nix { inherit pkgs; };
tar1090 = import ./tar1090.nix { inherit pkgs; };
};
}
)

58
packages/dump1090-fa.nix Normal file
View file

@ -0,0 +1,58 @@
{ pkgs, ... }:
let
pname = "dump1090-fa";
version = "8.2";
in
pkgs.stdenv.mkDerivation {
inherit pname version;
src = pkgs.fetchFromGitHub {
owner = "flightaware";
repo = "dump1090";
rev = "v${version}";
hash = "sha256-SUvK9XTXIDimEMEnORnp/Af/F030TZTxLI43Jzz31Js=";
};
outputs = [ "out" "html" ];
nativeBuildInputs = [
pkgs.pkg-config
];
propagatedBuildInputs = [
pkgs.ncurses
pkgs.librtlsdr
pkgs.libbladeRF
pkgs.limesuite
pkgs.hackrf
];
buildFlags = [
"DUMP1090_VERSION=v${version}"
"CPUFEATURES=yes"
"showconfig"
"dump1090"
"faup1090"
"view1090"
];
installPhase = ''
runHook preInstall
mkdir -p $out/bin
install -D dump1090 $out/bin
install -D faup1090 $out/bin
install -D view1090 $out/bin
mkdir -p $html
cp -a public_html/* $html
runHook postInstall
'';
meta = with pkgs.lib; {
homepage = "https://github.com/flightaware/dump1090";
description = "A ADS-B, Mode S, and Mode 3A/3C demodulator and decoder.";
longDescription = ''
dump1090-fa is a ADS-B, Mode S, and Mode 3A/3C demodulator and decoder that will receive and decode aircraft transponder messages received via a directly connected software defined radio, or from data provided over a network connection.
It is the successor to dump1090-mutability and is maintained by FlightAware.
It can provide a display of locally received aircraft data in a terminal or via a browser map. Together with PiAware it can be used to contribute crowd-sourced flight tracking data to FlightAware.
'';
license = licenses.gpl2Plus;
};
}

52
packages/dump978-fa.nix Normal file
View file

@ -0,0 +1,52 @@
{ pkgs, ... }:
let
pname = "dump978-fa";
version = "8.2";
in
pkgs.stdenv.mkDerivation {
inherit pname version;
src = pkgs.fetchFromGitHub {
owner = "flightaware";
repo = "dump978";
rev = "v${version}";
hash = "sha256-6LPshYbI/lLcjR/xP7DHFza2xVcfiAzh66Y7w95YDjk=";
};
outputs = [ "out" "html" ];
nativeBuildInputs = [
# pkgs.autoPatchelfHook
pkgs.pkg-config
];
propagatedBuildInputs = [
pkgs.boost
pkgs.soapysdr
];
buildFlags = [
"VERSION=v${version}"
"dump978-fa"
"faup978"
"skyaware978"
];
installPhase = ''
runHook preInstall
mkdir -p $out/bin
install -D dump978-fa $out/bin
install -D faup978 $out/bin
install -D skyaware978 $out/bin
mkdir -p $html
cp -a skyaware/* $html
runHook postInstall
'';
meta = with pkgs.lib; {
homepage = "https://github.com/flightaware/dump978";
description = "The FlightAware 978MHz UAT decoder..";
longDescription = ''
This is the FlightAware 978MHz UAT decoder.
It is a reimplementation in C++, loosely based on the demodulator from https://github.com/mutability/dump978.
'';
license = licenses.bsd2;
};
}

View file

@ -0,0 +1,14 @@
{ pkgs, ... }:
let
pname = "mlat-client";
version = "0.2.12";
in
pkgs.python3.pkgs.buildPythonApplication {
inherit pname version;
src = pkgs.fetchFromGitHub {
owner = "mutability";
repo = pname;
rev = "v${version}";
sha256 = "sha256-kU7DJ6lPwz+mVlp4g7rDz0TTSzsZvd/KxjHbYsDFLPI=";
};
}

91
packages/piaware.nix Normal file
View file

@ -0,0 +1,91 @@
{ pkgs, dump1090-fa, dump978-fa, tcllauncher, mutability-mlat-client, ... }:
let
pname = "piaware";
version = "8.2";
in
pkgs.stdenv.mkDerivation {
inherit pname version;
src = pkgs.fetchFromGitHub {
owner = "flightaware";
repo = "piaware";
rev = "v${version}";
sha256 = "sha256-La0J+6Y0cWr6fTr0ppzYV6Vq00GisyDxmSyGzR7nfpg=";
};
nativeBuildInputs = [
pkgs.makeWrapper
pkgs.which
];
propagatedBuildInputs = [
pkgs.coreutils
pkgs.iproute2
pkgs.openssl
pkgs.tcl
pkgs.tcltls
pkgs.tclx
tcllauncher
];
dontPatch = true;
dontConfigure = true;
buildPhase = ''
runHook preBuild
substituteInPlace programs/piaware/login.tcl \
--replace /bin/uname ${pkgs.coreutils}/bin/uname
substituteInPlace programs/piaware/helpers.tcl \
--replace /bin/uname ${pkgs.coreutils}/bin/uname
substituteInPlace programs/piaware/update.tcl \
--replace /sbin/reboot ${pkgs.systemd}/bin/reboot \
--replace /sbin/halt ${pkgs.systemd}/bin/halt
substituteInPlace programs/piaware/faup1090.tcl \
--replace /usr/lib/piaware/helpers/faup1090 ${dump1090-fa}/bin/faup1090
substituteInPlace programs/piaware/faup978.tcl \
--replace /usr/lib/piaware/helpers/faup978 ${dump978-fa}/bin/faup978
substituteInPlace programs/piaware/mlat.tcl \
--replace /usr/lib/piaware/helpers/fa-mutability-mlat-client ${mutability-mlat-client}/bin/fa-mutability-mlat-client
substituteInPlace package/fa_sysinfo.tcl \
--replace /sbin/ip ${pkgs.iproute2}/bin/ip \
--replace /bin/df ${pkgs.coreutils}/bin/df
substituteInPlace package/piaware.tcl \
--replace "list netstat --program" "list ${pkgs.nettools}/bin/netstat --program"
substituteInPlace package/helpers/restart-network \
--replace /bin/systemctl ${pkgs.systemd}/bin/systemctl
substituteInPlace package/helpers/restart-receiver \
--replace /bin/systemctl ${pkgs.systemd}/bin/systemctl
runHook postBuild
'';
installFlags = [
''DESTDIR=''${out}''
"PREFIX=/"
"INSTALL_SUDOERS=1"
];
postFixup =
let
tcllibpath = "$out/lib ${tcllauncher}/lib ${pkgs.tcl}/lib ${pkgs.tclx}/lib ${pkgs.tcltls}/lib ${pkgs.tcllib}/lib";
in
''
for f in piaware piaware-config piaware-status pirehose
do
ln -sf ${tcllauncher}/bin/tcllauncher $out/bin/$f
wrapProgram $out/bin/$f \
--set TCLLIBPATH "${tcllibpath}"
done
'';
meta = with pkgs.lib; {
homepage = "https://github.com/flightaware/piaware";
description = "";
longDescription = '' '';
license = licenses.gpl2Plus;
};
}

24
packages/tar1090.nix Normal file
View file

@ -0,0 +1,24 @@
{ pkgs, ... }:
let
pname = "tar1090";
version = "0.0";
in
pkgs.stdenvNoCC.mkDerivation {
inherit pname version;
src = pkgs.fetchFromGitHub {
owner = "wiedehopf";
repo = pname;
rev = "3c6a15369c4da2e327d08f7f57373abdf02c6812";
sha256 = "sha256-R4K4AxFQdy+Cc9gTVIpmU7r2ghiaDxxpFJcLUTwlhWY=";
};
dontPatch = true;
dontConfigure = true;
dontBuild = true;
dontFixup = true;
installPhase = ''
runHook preInstall
mkdir -p $out/html
cp -av html/* $out/html
runHook postInstall
'';
}

63
packages/tcllauncher.nix Normal file
View file

@ -0,0 +1,63 @@
{ pkgs, ... }:
let
pname = "tcllauncher";
version = "1.10";
in
pkgs.stdenv.mkDerivation {
inherit pname version;
src = pkgs.fetchFromGitHub {
owner = "flightaware";
repo = pname;
rev = "v${version}";
sha256 = "sha256-BVrsoczKeBBoM1Q3v6EJY81QwsX6xbUqFkcBb482WH4=";
};
nativeBuildInputs = [
pkgs.autoconf
pkgs.makeBinaryWrapper
];
propagatedBuildInputs = [
pkgs.tcl
pkgs.tclx
pkgs.tcllib
];
dontPatch = true;
preConfigure = ''
autoconf
'';
configureFlags = [
"--with-tcl=${pkgs.tcl}/lib"
];
buildFlags = [
''pkglibdir=''${out}/lib''
];
installFlags = [
''DESTDIR=''${out}''
"bindir=/bin"
"includedir=/include"
"mandir=/share/man"
"pkglibdir=/lib"
];
# postFixup = ''
# wrapProgram $out/bin/tcllauncher \
# --set TCLLIBPATH "${pkgs.tcl}/lib ${pkgs.tclx}/lib"
# '';
# buildFlags = [
# "DUMP1090_VERSION=${version}"
# "CPUFEATURES=yes"
# ];
# installPhase = ''
# runHook preInstall
# mkdir -p $out
# make DESTDIR=$out install
# runHook postInstall
# '';
meta = with pkgs.lib; {
homepage = "https://flightaware.github.io/tcllauncher/";
description = "Launcher program for Tcl applications.";
longDescription = ''
tcllauncher is a way to have Tcl programs run out of /usr/local/bin under their own name, be installed in one place with their support files, and provides commands to facilitate server-oriented application execution.
'';
license = licenses.gpl2Plus;
};
}