diff --git a/.env b/.env
new file mode 100644
index 0000000..75af76d
--- /dev/null
+++ b/.env
@@ -0,0 +1 @@
+CUDA_VISIBLE_DEVICES=""
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4f334a3
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+var/
+/.venv/
+/analyzer/
\ No newline at end of file
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..ab60bb3
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "BirdNET-Analyzer"]
+ path = analyzer
+ url = git@github.com:kahst/BirdNET-Analyzer.git
diff --git a/.ideas/create_database.sh b/.ideas/create_database.sh
new file mode 100755
index 0000000..943e910
--- /dev/null
+++ b/.ideas/create_database.sh
@@ -0,0 +1,21 @@
+#! /usr/bin/env bash
+
+# Load config file
+config_filepath="./config/analyzer.conf"
+
+if [ -f "$config_filepath" ]; then
+ source "$config_filepath"
+else
+ echo "Config file not found: $config_filepath"
+ exit 1
+fi
+
+# Check if database location is specified
+if [ -z "$DATABASE" ]; then
+ echo "DATABASE location not specified"
+ echo "Defaults to ./var/db.sqlite"
+ DATABASE="./var/db.sqlite"
+fi
+
+# Create database according to schema in structure.sql
+sqlite3 "$DATABASE" < ./daemon/database/structure.sql
\ No newline at end of file
diff --git a/.ideas/database_entity_model.svg b/.ideas/database_entity_model.svg
new file mode 100644
index 0000000..c8b2242
--- /dev/null
+++ b/.ideas/database_entity_model.svg
@@ -0,0 +1,202 @@
+
+
+
+
diff --git a/.ideas/fill_taxa.sh b/.ideas/fill_taxa.sh
new file mode 100755
index 0000000..b0348fe
--- /dev/null
+++ b/.ideas/fill_taxa.sh
@@ -0,0 +1,70 @@
+#! /usr/bin/env bash
+
+set -e
+
+# Load config file
+config_filepath="./config/analyzer.conf"
+
+if [ -f "$config_filepath" ]; then
+ source "$config_filepath"
+else
+ echo "Config file not found: $config_filepath"
+ exit 1
+fi
+
+# Check if database location is specified
+if [ -z "$DATABASE" ]; then
+ echo "DATABASE location not specified"
+ echo "Defaults to ./var/db.sqlite"
+ DATABASE="./var/db.sqlite"
+fi
+
+# Check if species list is specified
+if [ -z "$SPECIES_LIST" ]; then
+ echo "SPECIES_LIST location not specified"
+ exit 1
+fi
+
+function insert_taxa()
+{
+ for taxon in "$(cat $SPECIES_LIST)"; do
+ taxon_scientific_name=$(echo $taxon | cut -d'_' -f1)
+ taxon_common_name=$(echo $taxon | cut -d'_' -f2)
+ if $(taxon_exists $taxon_scientific_name); then
+ echo "Taxon already exists: $taxon_scientific_name"
+ else
+ echo "Inserting taxon: $taxon_scientific_name"
+ statement="INSERT INTO taxon (scientific_name, common_name) VALUES ('$taxon_scientific_name', '$taxon_common_name')"
+ echo $statement
+ result=$(sqlite3 "$DATABASE" "$statement")
+ echo "$result"
+ fi
+ done
+}
+
+function taxon_exists()
+{
+ taxon_scientific_name="$1"
+ statement="SELECT scientific_name FROM taxon WHERE scientific_name='$taxon_scientific_name'"
+ result=$(sqlite3 "$DATABASE" "$statement")
+ if [ -z "$result" ]; then
+ return 1
+ else
+ return 0
+ fi
+}
+
+function purge_taxa()
+{
+ statement="DELETE FROM taxon"
+ result=$(sqlite3 "$DATABASE" "$statement")
+ echo "$result"
+}
+
+function main()
+{
+ purge_taxa
+ insert_taxa
+}
+
+main
\ No newline at end of file
diff --git a/.ideas/insert_observation.sh b/.ideas/insert_observation.sh
new file mode 100644
index 0000000..73123d9
--- /dev/null
+++ b/.ideas/insert_observation.sh
@@ -0,0 +1,41 @@
+#! /usr/bin/env bash
+
+# Load config file
+config_filepath="./config/analyzer.conf"
+
+if [ -f "$config_filepath" ]; then
+ source "$config_filepath"
+else
+ echo "Config file not found: $config_filepath"
+ exit 1
+fi
+
+# Check if database location is specified
+if [ -z "$DATABASE" ]; then
+ echo "DATABASE location not specified"
+ echo "Defaults to ./var/db.sqlite"
+ DATABASE="./var/db.sqlite"
+fi
+
+function insert_observation()
+{
+ # Insert observation into database
+ template=$(cat ./daemon/database/observation_template.sql)
+ statement=$(echo "$template" | sed "s/:taxon_id/$1/g" | sed "s/:location_id/$2/g" | sed "s/:date/$3/g" | sed "s/:time/$4/g" | sed "s/:confidence/$5/g" | sed "s/:notes/$6/g")
+ result=$(sqlite3 "$DATABASE" $statement)
+ echo "$result"
+}
+
+function get_taxon_id()
+{
+ # Get taxon id from database
+ statement="SELECT taxon_id FROM taxon WHERE scientific_name='$1'"
+ result=$(sqlite3 "$DATABASE" "$statement")
+ echo "$result"
+}
+
+function test()
+{
+ taxon_scientific_name="Erithacus rubecula"
+ taxon_id=$(get_taxon_id "$taxon_scientific_name")
+}
\ No newline at end of file
diff --git a/.ideas/observation_template.sql b/.ideas/observation_template.sql
new file mode 100644
index 0000000..57c0f14
--- /dev/null
+++ b/.ideas/observation_template.sql
@@ -0,0 +1,3 @@
+/** Observation database entry template */
+
+INSERT INTO observation (taxon_id, locality_id, date, time, confidence) VALUES (:taxon_id, :locality_id, :date, :time, :confidence);
\ No newline at end of file
diff --git a/INSTALL.md b/INSTALL.md
new file mode 100644
index 0000000..7a793ff
--- /dev/null
+++ b/INSTALL.md
@@ -0,0 +1,37 @@
+# Installation Guide for BirdNET-stream
+
+## Requirements
+
+- git
+- ffmpeg
+- python3
+
+## Install process
+
+### Install python requirements
+
+```bash
+sudo apt-get update
+sudo apt-get install python3-dev python3-pip
+sudo pip3 install --upgrade pip
+```
+
+### Install ffmpeg
+
+```bash
+sudo apt-get install ffmpeg
+```
+
+### Clone BirdNET-stream repository
+
+```bash
+git clone https://forge.chapril.org/UncleSamulus/BirdNET-stream.git
+
+### Setup python virtualenv and packages
+
+```bash
+python3 -m venv .venv/birdnet-stream
+source .venv/birdnet-stream
+
+pip install -r requirements.txt
+```
diff --git a/README.md b/README.md
index a6aaaae..7a6be76 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,14 @@
# BirdNET-stream
-BirdNET powered soundscape analysis for bird song identification
\ No newline at end of file
+
+
+
+
+Realtime BirdNET powered soundscape analysis for bird song identification.
+
+
+## Acknoledgements
+
+- [BirdNET](https://birdnet.cornell.edu) on which this project relies
+- [BirdNET-Pi](https://birdnetpi.com) the great inspiration of this project
+
diff --git a/analyzer b/analyzer
new file mode 160000
index 0000000..08f0315
--- /dev/null
+++ b/analyzer
@@ -0,0 +1 @@
+Subproject commit 08f031585265e5bc22e86a60ecbb0310c937a29b
diff --git a/config/analyzer.conf b/config/analyzer.conf
new file mode 100644
index 0000000..80ef0a6
--- /dev/null
+++ b/config/analyzer.conf
@@ -0,0 +1,20 @@
+# Configuration file for BirdNET Analyzer
+
+# Coordinates of the recorder
+LATITUDE="47.87842"
+LONGITUDE="0.21826"
+LOCATION="Maison ORTION - Saint-Gervais-en-Belin"
+# Species selection list
+SPECIES_LIST="./config/species_list.txt"
+# Minimal confidence threshold
+CONFIDENCE=0.25
+# Recording duration (in seconds)
+RECORDING_DURATION=15
+# Chunk folder location
+CHUNK_FOLDER="./var/chunks"
+# Audio recording device (pulseaudio)
+AUDIO_DEVICE="default"
+# Virtual env for BirdNET AI with required packages
+PYTHON_VENV="./.venv/birdnet-stream"
+# Database location
+DATABASE="./var/db.sqlite"
diff --git a/config/species_list.txt b/config/species_list.txt
new file mode 100644
index 0000000..169c290
--- /dev/null
+++ b/config/species_list.txt
@@ -0,0 +1,189 @@
+Acanthis cabaret_Lesser Redpoll
+Accipiter nisus_Eurasian Sparrowhawk
+Acrocephalus palustris_Marsh Warbler
+Acrocephalus schoenobaenus_Sedge Warbler
+Acrocephalus scirpaceus_Eurasian Reed Warbler
+Actitis hypoleucos_Common Sandpiper
+Aegithalos caudatus_Long-tailed Tit
+Aix galericulata_Mandarin Duck
+Alauda arvensis_Eurasian Skylark
+Alcedo atthis_Common Kingfisher
+Alectoris rufa_Red-legged Partridge
+Alopochen aegyptiaca_Egyptian Goose
+Anas acuta_Northern Pintail
+Anas crecca_Green-winged Teal
+Anas platyrhynchos_Mallard
+Anser albifrons_Greater White-fronted Goose
+Anser anser_Graylag Goose
+Anthus petrosus_Rock Pipit
+Anthus pratensis_Meadow Pipit
+Anthus spinoletta_Water Pipit
+Anthus trivialis_Tree Pipit
+Apus apus_Common Swift
+Aquila chrysaetos_Golden Eagle
+Ardea alba_Great Egret
+Ardea cinerea_Gray Heron
+Ardea purpurea_Purple Heron
+Arenaria interpres_Ruddy Turnstone
+Aythya ferina_Common Pochard
+Aythya fuligula_Tufted Duck
+Aythya marila_Greater Scaup
+Branta bernicla_Brant
+Branta canadensis_Canada Goose
+Bubulcus ibis_Cattle Egret
+Bucephala clangula_Common Goldeneye
+Buteo buteo_Common Buzzard
+Calidris alpina_Dunlin
+Calidris melanotos_Pectoral Sandpiper
+Calidris pugnax_Ruff
+Carduelis carduelis_European Goldfinch
+Certhia brachydactyla_Short-toed Treecreeper
+Certhia familiaris_Eurasian Treecreeper
+Cettia cetti_Cetti's Warbler
+Charadrius dubius_Little Ringed Plover
+Charadrius hiaticula_Common Ringed Plover
+Chlidonias hybrida_Whiskered Tern
+Chloris chloris_European Greenfinch
+Chroicocephalus ridibundus_Black-headed Gull
+Ciconia ciconia_White Stork
+Cinclus cinclus_White-throated Dipper
+Circaetus gallicus_Short-toed Snake-Eagle
+Circus aeruginosus_Eurasian Marsh-Harrier
+Circus pygargus_Montagu's Harrier
+Cisticola juncidis_Zitting Cisticola
+Coccothraustes coccothraustes_Hawfinch
+Columba livia_Rock Pigeon
+Columba oenas_Stock Dove
+Columba palumbus_Common Wood-Pigeon
+Corvus corax_Common Raven
+Corvus corone_Carrion Crow
+Corvus frugilegus_Rook
+Corvus monedula_Eurasian Jackdaw
+Coturnix coturnix_Common Quail
+Cuculus canorus_Common Cuckoo
+Curruca communis_Greater Whitethroat
+Curruca curruca_Lesser Whitethroat
+Curruca undata_Dartford Warbler
+Cyanistes caeruleus_Eurasian Blue Tit
+Cygnus olor_Mute Swan
+Delichon urbicum_Common House-Martin
+Dendrocopos major_Great Spotted Woodpecker
+Dendrocoptes medius_Middle Spotted Woodpecker
+Dryobates minor_Lesser Spotted Woodpecker
+Dryocopus martius_Black Woodpecker
+Egretta garzetta_Little Egret
+Emberiza calandra_Corn Bunting
+Emberiza cirlus_Cirl Bunting
+Emberiza citrinella_Yellowhammer
+Emberiza schoeniclus_Reed Bunting
+Erithacus rubecula_European Robin
+Falco peregrinus_Peregrine Falcon
+Falco subbuteo_Eurasian Hobby
+Falco tinnunculus_Eurasian Kestrel
+Ficedula hypoleuca_European Pied Flycatcher
+Fringilla coelebs_Common Chaffinch
+Fringilla montifringilla_Brambling
+Fulica atra_Eurasian Coot
+Gallinago gallinago_Common Snipe
+Gallinula chloropus_Eurasian Moorhen
+Garrulus glandarius_Eurasian Jay
+Grus grus_Common Crane
+Haematopus ostralegus_Eurasian Oystercatcher
+Himantopus himantopus_Black-winged Stilt
+Hippolais polyglotta_Melodious Warbler
+Hirundo rustica_Barn Swallow
+Ichthyaetus melanocephalus_Mediterranean Gull
+Lanius collurio_Red-backed Shrike
+Larus argentatus_Herring Gull
+Larus canus_Common Gull
+Larus fuscus_Lesser Black-backed Gull
+Larus marinus_Great Black-backed Gull
+Larus michahellis_Yellow-legged Gull
+Limosa lapponica_Bar-tailed Godwit
+Limosa limosa_Black-tailed Godwit
+Linaria cannabina_Eurasian Linnet
+Locustella naevia_Common Grasshopper-Warbler
+Lophophanes cristatus_Crested Tit
+Loxia curvirostra_Red Crossbill
+Lullula arborea_Wood Lark
+Luscinia megarhynchos_Common Nightingale
+Luscinia svecica_Bluethroat
+Mareca penelope_Eurasian Wigeon
+Mareca strepera_Gadwall
+Mergus merganser_Common Merganser
+Milvus migrans_Black Kite
+Milvus milvus_Red Kite
+Morus bassanus_Northern Gannet
+Motacilla alba_White Wagtail
+Motacilla cinerea_Gray Wagtail
+Motacilla flava_Western Yellow Wagtail
+Muscicapa striata_Spotted Flycatcher
+Numenius arquata_Eurasian Curlew
+Nycticorax nycticorax_Black-crowned Night-Heron
+Oenanthe oenanthe_Northern Wheatear
+Oriolus oriolus_Eurasian Golden Oriole
+Pandion haliaetus_Osprey
+Panurus biarmicus_Bearded Reedling
+Parus major_Great Tit
+Passer domesticus_House Sparrow
+Passer montanus_Eurasian Tree Sparrow
+Perdix perdix_Gray Partridge
+Periparus ater_Coal Tit
+Pernis apivorus_European Honey-buzzard
+Phalacrocorax carbo_Great Cormorant
+Phasianus colchicus_Ring-necked Pheasant
+Phoenicurus ochruros_Black Redstart
+Phoenicurus phoenicurus_Common Redstart
+Phylloscopus bonelli_Western Bonelli's Warbler
+Phylloscopus collybita_Common Chiffchaff
+Phylloscopus ibericus_Iberian Chiffchaff
+Phylloscopus trochilus_Willow Warbler
+Pica pica_Eurasian Magpie
+Picus viridis_Eurasian Green Woodpecker
+Pluvialis apricaria_European Golden-Plover
+Pluvialis squatarola_Black-bellied Plover
+Podiceps cristatus_Great Crested Grebe
+Poecile montanus_Willow Tit
+Poecile palustris_Marsh Tit
+Prunella modularis_Dunnock
+Psittacula krameri_Rose-ringed Parakeet
+Pyrrhocorax pyrrhocorax_Red-billed Chough
+Pyrrhula pyrrhula_Eurasian Bullfinch
+Rallus aquaticus_Water Rail
+Recurvirostra avosetta_Pied Avocet
+Regulus ignicapilla_Common Firecrest
+Regulus regulus_Goldcrest
+Remiz pendulinus_Eurasian Penduline-Tit
+Riparia riparia_Bank Swallow
+Saxicola rubetra_Whinchat
+Saxicola rubicola_European Stonechat
+Serinus serinus_European Serin
+Sitta europaea_Eurasian Nuthatch
+Spatula clypeata_Northern Shoveler
+Spatula querquedula_Garganey
+Spinus spinus_Eurasian Siskin
+Sterna hirundo_Common Tern
+Sternula albifrons_Little Tern
+Streptopelia decaocto_Eurasian Collared-Dove
+Streptopelia turtur_European Turtle-Dove
+Strix aluco_Tawny Owl
+Sturnus vulgaris_European Starling
+Sylvia atricapilla_Eurasian Blackcap
+Sylvia borin_Garden Warbler
+Tachybaptus ruficollis_Little Grebe
+Tadorna tadorna_Common Shelduck
+Thalasseus sandvicensis_Sandwich Tern
+Tringa erythropus_Spotted Redshank
+Tringa glareola_Wood Sandpiper
+Tringa nebularia_Common Greenshank
+Tringa ochropus_Green Sandpiper
+Tringa totanus_Common Redshank
+Troglodytes troglodytes_Eurasian Wren
+Turdus iliacus_Redwing
+Turdus merula_Eurasian Blackbird
+Turdus philomelos_Song Thrush
+Turdus pilaris_Fieldfare
+Turdus viscivorus_Mistle Thrush
+Tyto alba_Barn Owl
+Uria aalge_Common Murre
+Vanellus vanellus_Northern Lapwing
diff --git a/daemon/analyze-stream.sh b/daemon/analyze-stream.sh
new file mode 100755
index 0000000..8230e9d
--- /dev/null
+++ b/daemon/analyze-stream.sh
@@ -0,0 +1,90 @@
+#! /usr/bin/env bash
+set -e
+
+config_filepath="./config/analyzer.conf"
+
+if [ -f "$config_filepath" ]; then
+ source "$config_filepath"
+else
+ echo "Config file not found: $config_filepath"
+ exit 1
+fi
+
+PYTHON_EXECUTABLE="${PYTHON_VENV}/bin/python3"
+
+check_prerequisites() {
+ if [[ -z ${LATITUDE} ]]; then
+ echo "LATITUDE is not set"
+ exit 1
+ fi
+ if [[ -z ${LONGITUDE} ]]; then
+ echo "LONGITUDE is not set"
+ exit 1
+ fi
+ if [[ -z ${CHUNK_FOLDER} ]]; then
+ echo "CHUNK_FOLDER is not set"
+ exit 1
+ else
+ if [[ ! -d "${CHUNK_FOLDER}" ]]; then
+ echo "CHUNK_FOLDER does not exist: ${CHUNK_FOLDER}"
+ exit 1
+ else
+ if [[ ! -d "${CHUNK_FOLDER}/in" ]]; then
+ echo "Input dir does not exist: ${CHUNK_FOLDER}/in"
+ exit 1
+ else
+ if [[ ! -d "${CHUNK_FOLDER}/out" ]]; then
+ echo "Output dir does not exist: ${CHUNK_FOLDER}/out"
+ echo "Creating output dir"
+ mkdir -p "${CHUNK_FOLDER}/out"
+ fi
+ fi
+ fi
+ fi
+ fi
+ if [[ -z ${SPECIES_LIST} ]];
+ then
+ echo "SPECIES_LIST is not set"
+ exit 1
+ fi
+ if [[ -f $PYTHON_EXECUTABLE ]];
+ then
+ if $verbose; then
+ echo "Python executable found: $PYTHON_EXECUTABLE"
+ fi
+ else
+ echo "Python executable not found: $PYTHON_EXECUTABLE"
+ exit 1
+ fi
+}
+
+# Get array of audio chunks to be processed
+get_chunk_list() {
+ find "$CHUNK_FOLDER/in" -type f -name '*.wav' -exec basename {} \; ! -size 0 | sort
+}
+
+# Perform audio chunk analysis on one chunk
+analyze_chunk() {
+ chunk_name=$1
+ chunk_path="$CHUNK_FOLDER/in/$chunk_name"
+ output_dir="$CHUNK_FOLDER/out/$chunk_name.d"
+ mkdir -p "$output_dir"
+ date=$(echo $chunk_name | cut -d'_' -f2)
+ week=$(./daemon/weekof.sh $date)
+ $PYTHON_EXECUTABLE ./analyzer/analyze.py --i $chunk_path --o "$output_dir/model.out.csv" --lat $LATITUDE --lon $LONGITUDE --week $week --min_conf $CONFIDENCE --threads 4 --rtype csv
+}
+
+# Perform audio chunk analysis on all recorded chunks
+analyze_chunks() {
+ for chunk_name in $(get_chunk_list); do
+ analyze_chunk $chunk_name
+ chunk_path="$CHUNK_FOLDER/in/$chunk_name"
+ mv $chunk_path "$CHUNK_FOLDER/out/$chunk_name"
+ done
+}
+
+# Get list of current chunk in working directory
+chunks=$(get_chunk_list)
+
+# Analyze all chunks in working directory
+analyze_chunks $chunks
\ No newline at end of file
diff --git a/daemon/database/scripts/database.py b/daemon/database/scripts/database.py
new file mode 100755
index 0000000..b56ab9e
--- /dev/null
+++ b/daemon/database/scripts/database.py
@@ -0,0 +1,83 @@
+#! /usr/bin/env python3
+
+import sqlite3
+import os
+
+verbose = False
+
+"""Load config"""
+def load_conf():
+ with open("./config/analyzer.conf", "r") as f:
+ conf = f.readlines()
+ res = dict(map(str.strip, sub.split('=', 1)) for sub in conf if '=' in sub)
+ return res
+
+# Singleton database instance
+database = None
+def get_database():
+ global database
+ if database is None:
+ database = sqlite3.connect(CONFIG["DATABASE"])
+ return database
+
+"""Create the database if it doesn't exist"""
+def create_database():
+ # Create the database
+ database =
+
+ database = get_database()
+ cursor = database.cursor()
+ with open("./daemon/database/structure.sql", "r") as f:
+ cursor.executescript(f.read())
+ database.commit()
+
+"""Insert an observation into the database"""
+def insert_observation(observation):
+ database = get_database()
+ cursor = database.cursor()
+ cursor.execute(f"INSERT INTO observation (taxon_id, locality_id, date, time, confidence) VALUES ({observation['taxon_id']}, {observation['locality_id']}, {observation['date']}, {observation['time']}, {observation['confidence']});")
+ database.commit()
+
+"""Insert a taxon in database"""
+def insert_taxon(taxon):
+ database = get_database()
+ cursor = database.cursor()
+ cursor.execute(f"INSERT INTO taxon (scientific_name, common_name) VALUES ('{taxon['scientific_name']}', '{taxon['common_name']}');")
+ database.commit()
+
+"""Insert a location into database"""
+def insert_locality(locality):
+ database = get_database()
+ cursor = database.cursor()
+ cursor.execute(f"INSERT INTO locality (name, latitude, longitude) VALUES ('{locality['name']}', {locality['latitude']}, {locality['longitude']});")
+ database.commit()
+
+"""Insert all species from list into database"""
+def insert_all_species(species):
+ database = get_database()
+ cursor = database.cursor()
+ for sp in species:
+ # Check if the species already exists in the database
+ cursor.execute(f"SELECT * FROM taxon WHERE scientific_name = '{sp[0]}';")
+ # If it doesn't exist, insert it
+ if cursor.fetchone() is None:
+ cursor.execute(f"INSERT INTO taxon (scientific_name, common_name) VALUES ('{sp[0]}', '{sp[1]}');")
+ database.commit()
+
+CONFIG = load_conf()
+
+def main():
+ # Create the database if it doesn't exist
+ if not os.path.exists(CONFIG["DATABASE"]):
+ create_database()
+ else:
+ print("Database already exists")
+
+ # Open species list file
+ with open(CONFIG["SPECIES_LIST"], "r") as f:
+ species = f.readlines()
+ species = [sp.split("_") for sp in species]
+
+if __name__ == "__main__":
+ main()
+ database.close()
\ No newline at end of file
diff --git a/daemon/database/structure.sql b/daemon/database/structure.sql
new file mode 100644
index 0000000..fd736e0
--- /dev/null
+++ b/daemon/database/structure.sql
@@ -0,0 +1,29 @@
+/** Database structure for BirdNET-stream SQLite*/
+
+/** Taxon table */
+CREATE TABLE IF NOT EXISTS taxon (
+ taxon_id INTEGER PRIMARY KEY,
+ scientific_name TEXT NOT NULL,
+ common_name TEXT NOT NULL
+);
+
+/** Locality table */
+CREATE TABLE IF NOT EXISTS locality (
+ locality_id INTEGER PRIMARY KEY,
+ name TEXT NOT NULL,
+ latitude REAL NOT NULL,
+ longitude REAL NOT NULL
+);
+
+/** Observation table */
+CREATE TABLE IF NOT EXISTS observation (
+ observation_id INTEGER PRIMARY KEY,
+ taxon_id INTEGER NOT NULL,
+ locality_id INTEGER NOT NULL,
+ date TEXT NOT NULL,
+ time TEXT NOT NULL,
+ notes TEXT,
+ confidence REAL NOT NULL,
+ FOREIGN KEY(taxon_id) REFERENCES taxon(taxon_id),
+ FOREIGN KEY(locality_id) REFERENCES locality(locality_id)
+);
\ No newline at end of file
diff --git a/daemon/dist/birdnet-stream.service b/daemon/dist/birdnet-stream.service
new file mode 100644
index 0000000..e69de29
diff --git a/daemon/miner.sh b/daemon/miner.sh
new file mode 100644
index 0000000..997dd16
--- /dev/null
+++ b/daemon/miner.sh
@@ -0,0 +1,35 @@
+# !/bin/bash
+
+# Extract data generated with BirdNET on record to get relevant informations and record data in sqlite
+
+# Load config file
+config_filepath="./config/analyzer.conf"
+if [ -f "$config_filepath" ]; then
+ source "$config_filepath"
+else
+ echo "Config file not found: $config_filepath"
+ exit 1
+fi
+
+# Verify needed prerequisites
+if [[ -z ${CHUNK_FOLDER} ]]; then
+ echo "CHUNK_FOLDER is not set"
+ exit 1
+else
+ if [[ ! -d "${CHUNK_FOLDER}" ]]; then
+ echo "CHUNK_FOLDER does not exist: ${CHUNK_FOLDER}"
+ exit 1
+ else
+ if [[ ! -d "${CHUNK_FOLDER}/out" ]]; then
+ echo "Output dir does not exist: ${CHUNK_FOLDER}/out"
+ echo "Cannot mine data"
+ exit 1
+ fi
+ fi
+ fi
+fi
+
+function list_all_model_outputs()
+{
+
+}
\ No newline at end of file
diff --git a/daemon/record-chunks.sh b/daemon/record-chunks.sh
new file mode 100755
index 0000000..bfd6e63
--- /dev/null
+++ b/daemon/record-chunks.sh
@@ -0,0 +1,28 @@
+#! /usr/bin/env bash
+
+record_chunk()
+{
+ DEVICE=$1
+ DURATION=$2
+ ffmpeg -f pulse -i ${DEVICE} -t ${DURATION} -vn -acodec pcm_s16le -ac 1 -ar 48000 file:${CHUNK_FOLDER}/in/birdnet_$(date "+%Y%m%d_%H%M%S").wav
+}
+
+config_filepath="./config/analyzer.conf"
+
+if [ -f "$config_filepath" ]; then
+ source "$config_filepath"
+else
+ echo "Config file not found: $config_filepath"
+ exit 1
+fi
+
+[ -z $RECORDING_DURATION ] && RECORDING_DURATION=15
+
+if [[ -z $AUDIO_DEVICE ]]; then
+ echo "AUDIO_DEVICE is not set"
+ exit 1
+fi
+
+while true; do
+ record_chunk $AUDIO_DEVICE $RECORDING_DURATION
+done
\ No newline at end of file
diff --git a/daemon/weekof.sh b/daemon/weekof.sh
new file mode 100755
index 0000000..e3fb93f
--- /dev/null
+++ b/daemon/weekof.sh
@@ -0,0 +1,9 @@
+#! /usr/bin/env bash
+
+function weekof()
+{
+ local date=$1
+ date -d "$date" +%W
+}
+
+weekof $1
\ No newline at end of file
diff --git a/media/logo.svg b/media/logo.svg
new file mode 100644
index 0000000..b3aa02e
--- /dev/null
+++ b/media/logo.svg
@@ -0,0 +1,231 @@
+
+
+
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..9bc73ef
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,24 @@
+appdirs==1.4.4
+audioread==2.1.9
+certifi==2022.6.15
+cffi==1.15.1
+charset-normalizer==2.1.0
+decorator==5.1.1
+idna==3.3
+joblib==1.1.0
+librosa==0.9.2
+llvmlite==0.39.0
+numba==0.56.0
+numpy==1.22.4
+packaging==21.3
+pooch==1.6.0
+pycparser==2.21
+pyparsing==3.0.9
+requests==2.28.1
+resampy==0.3.1
+scikit-learn==1.1.2
+scipy==1.9.0
+SoundFile==0.10.3.post1
+tflite-runtime==2.9.1
+threadpoolctl==3.1.0
+urllib3==1.26.11
diff --git a/utils/purge.sh b/utils/purge.sh
new file mode 100755
index 0000000..dcab0ee
--- /dev/null
+++ b/utils/purge.sh
@@ -0,0 +1,24 @@
+#! /usr/bin/env bash
+
+# Load config file
+config_filepath="./config/analyzer.conf"
+
+if [ -f "$config_filepath" ]; then
+ source "$config_filepath"
+else
+ echo "Config file not found: $config_filepath"
+ exit 1
+fi
+
+# Remove all files from the temporary record directory
+function clean_record_dir()
+{
+ rm -rf "$CHUNK_FOLDER/in/*.wav"
+}
+
+function remove_empty_records()
+{
+ find $CHUNK_FOLDER/in -maxdepth 1 -name '*wav' -type f -size 0 -delete
+}
+
+remove_empty_records
\ No newline at end of file