diff --git a/Birders_Guide_Installer.sh b/Birders_Guide_Installer.sh deleted file mode 100755 index 01b4d68..0000000 --- a/Birders_Guide_Installer.sh +++ /dev/null @@ -1,300 +0,0 @@ -#!/usr/bin/env bash -set -e -USER=pi -HOME=/home/pi -my_dir=${HOME}/BirdNET-Pi -branch=main -trap '${my_dir}/scripts/dump_logs.sh && exit' EXIT SIGHUP SIGINT - -if [ "$(uname -m)" != "aarch64" ];then - echo "BirdNET-Pi requires a 64-bit OS. -It looks like your operating system is using $(uname -m), -but would need to be aarch64. -Please take a look at https://birdnetwiki.pmcgui.xyz for more -information" - exit 1 -fi - -stage_1() { - echo - echo "Beginning Stage 1" - echo - echo "Ensuring the system is up-to-date." - sudo apt -qq update - sudo apt -qqy full-upgrade - sudo apt -y autoremove --purge - echo "System Updated!" - if ! which git &> /dev/null; then - echo "Installing git" - sudo apt install -qqy git - fi - touch ${HOME}/stage_1_complete - echo "Stage 1 complete" -} - -stage_2() { - echo - echo "Beginning stage 2" - echo - echo "Checking for an internet connection to continue . . ." - until ping -c 1 8.8.8.8 &> /dev/null; do - sleep 1 - done - echo "Connected!" - echo - if [ ! -d ${my_dir} ];then - cd ${HOME} || exit 1 - echo "Cloning the BirdNET-Pi repository $branch branch into your home directory" - git clone -b ${branch} https://github.com/mcguirepr89/BirdNET-Pi.git ${HOME}/BirdNET-Pi - else - cd ${my_dir} && git checkout ${branch} - fi - - source ${my_dir}/Birders_Guide_Installer_Configuration.txt - if [ -z ${LATITUDE} ] || [ -z ${LONGITUDE} ] || [ -z ${CADDY_PWD} ] || [ -z ${ICE_PWD} ] || [ -z ${DB_PWD} ];then - echo - echo "Follow the instructions to fill out the LATITUDE and LONGITUDE variables -and set the passwords for the live audio stream. Save the file after editing -and then close the Mouse Pad editing window to continue." - echo - if [ -z "$SSH_CONNECTION" ];then - EDITOR=mousepad - else - EDITOR=nano - fi - $EDITOR ${my_dir}/Birders_Guide_Installer_Configuration.txt - while pgrep $EDITOR &> /dev/null;do - sleep 1 - done - source ${my_dir}/Birders_Guide_Installer_Configuration.txt || exit 1 - fi - echo "Installing the BirdNET-Pi configuration file." - install_birdnet_config || exit 1 - echo "Installing BirdNET-Lite" - if ${my_dir}/scripts/install_birdnet.sh;then -echo "The next time you power on the raspberry pi, all of the services will start up automatically. - -The installation has finished. Press Enter to close this window." - read - else - echo "Something went wrong during installation. Open a github issue or email mcguirepr89@gmail.com" - fi -} - -install_birdnet_config() { - cat << EOF > ${my_dir}/birdnet.conf -################################################################################ -# Configuration settings for BirdNET-Pi # -################################################################################ - -############ CHANGE THE LATITUDE AND LONGITUDE TO STATIC VALUES ################ - - -## LATITUDE and LONGITUDE are self-explanatroy. Find them easily at -## maps.google.com. -## Example: these coordinates would indicate the Eiffel Tower in Paris, France. -## LATITUDE=48.858 -## LONGITUDE=2.294 - -## These are input as shell substitutions so that this will work out of the box. -## It guesses your latitude and longitude based off of your network information. -## THESE SHOULD BE CHANGED TO STATIC NUMBERS!!!! -LATITUDE=${LATITUDE} -LONGITUDE=${LONGITUDE} - -## RECS_DIR is the location birdnet_analysis.service will look for the data-set -## it needs to analyze. Be sure this directory is readable and writable for -## the BIRDNET_USER. If you are going to be accessing a remote data-set, you -## still need to set this, as this will be where the remote directory gets -## mounted locally. - -RECS_DIR=/home/pi/BirdSongs - -#----------------------- Web Interface User Password ------------------------# -#____________________The variable below sets the 'birdnet'_____________________# -#___________________user password for the live audio stream,___________________# -#_________________web tools, system info, and processed files__________________# - -## CADDY_PWD is the plaintext password (that will be hashed) and used to access -## certain parts of the web interface - -CADDY_PWD=changeme - -#------------------------- MariaDB User Passwords ---------------------------# -#_____________The variable below sets the password for the_____________________# -#_______________________'birder' user on the MariaDB___________________________# - -## DB_PWD is for the 'birder' user -DB_PWD=changeme - -#------------------------- Live Audio Stream --------------------------------# -#_____________The variable below configures/enables the live___________________# -#_____________________________audio stream.____________________________________# - - -## ICE_PWD is the password that icecast2 will use to authenticate ffmpeg as a -## trusted source for the stream. You will never need to enter this manually -## anywhere other than here. - -ICE_PWD=changeme - -#--------------------- BirdWeather Station Information -----------------------# -#_____________The variable below can be set to have your BirdNET-Pi____________# -#__________________also act as a BirdWeather listening station_________________# - -BIRDWEATHER_ID=${BIRDWEATHER_ID} - -#----------------------- Web-hosting/Caddy File-server -----------------------# -#________The four variables below can be set to enable internet access_________# -#____________to your data,(e.g., extractions, raw data, live___________________# -#______________audio stream, BirdNET.selection.txt files)______________________# - - -## BIRDNETPI_URL is the URL where the extractions, data-set, and live-stream -## will be web-hosted. If you do not own a domain, or would just prefer to keep -## the BirdNET-Pi on your local network, keep this EMPTY. - -BIRDNETPI_URL=${BIRDNETPI_URL} -EXTRACTIONLOG_URL=${EXTRACTIONLOG_URL} -BIRDNETLOG_URL=${BIRDNETLOG_URL} - - -#------------------- Mobile Notifications via Pushed.co ---------------------# -#____________The two variables below enable mobile notifications_______________# -#_____________See https://pushed.co/quick-start-guide to get___________________# -#_________________________these values for your app.___________________________# - -# Keep these EMPTY if haven't setup a Pushed.co App yet. # - -## Pushed.co App Key and App Secret - -PUSHED_APP_KEY=${PUSHED_APP_KEY} -PUSHED_APP_SECRET=${PUSHED_APP_SECRET} - -################################################################################ -#-------------------------------- Defaults ----------------------------------# -################################################################################ - -#------------------------------- NoMachine ----------------------------------# -#_____________The variable below can be set include NoMachine__________________# -#_________________remote desktop software to be installed._____________________# - -# Keep this EMPTY if you do not want to install NoMachine. # - -## INSTALL_NOMACHINE is simply a setting that can be enabled to install -## NoMachine alongside the BirdNET-Pi for remote desktop access. This in- -## staller assumes personal use. Please reference the LICENSE file included -## in this repository for more information. -## Set this to Y or y to install NoMachine alongside the BirdNET-Lite - -INSTALL_NOMACHINE=y - -# -#------------------------------ Extraction Service ---------------------------# - -## DO_EXTRACTIONS is simply a setting for enabling the extraction.service. -## Set this to Y or y to enable extractions. - -DO_EXTRACTIONS=y - -#----------------------------- Recording Service ----------------------------# -#____________________The variable below can be set to enable __________________# -#________________________the birdnet_recording.service ________________________# - -## DO_RECORDING is simply a setting for enabling the 24/7 -## birdnet_recording.service. -## Set this to Y or y to enable recording. - -DO_RECORDING=y - -## REC_CARD is the sound card you would want the birdnet_recording.service to -## use. This setting is irrelevant if you are not planning on doing data -## collection via recording on this machine. The command substitution below -## looks for a USB microphone's dsnoop alsa device. The dsnoop device lets -## birdnet_recording.service and livestream.service share the raw audio stream -## from the microphone. If you would like to use a different microphone than -## what this produces, or if your microphone does not support creating a -## dsnoop device, you can set this explicitly from a list of the available -## devices from the output of running 'aplay -L' - -REC_CARD=default - -## PROCESSED is the directory where the formerly 'Analyzed' files are moved -## after extractions have been made from them. This includes both WAVE and -## BirdNET.selection.txt files. - -PROCESSED=/home/pi/BirdSongs/Processed - -## EXTRACTED is the directory where the extracted audio selections are moved. - -EXTRACTED=/home/pi/BirdSongs/Extracted - -## IDFILE is the file that keeps a complete list of every spececies that -## BirdNET has identified from your data-set. It is persistent across -## data-sets, so would need to be whiped clean through deleting or renaming -## it. A backup is automatically made from this variable each time it is -## updated (structure: ${IDFILE}.bak), and would also need to be removed -## or renamed to start a new file between data-sets. Alternately, you can -## change this variable between data-sets to preserve records of disparate -## data-sets according to name. - -IDFILE=${HOME}/BirdNET-Pi/IdentifiedSoFar.txt - -## OVERLAP is the value in seconds which BirdNET should use when analyzing -## the data. The values must be between 0.0-2.9. - -OVERLAP=0.0 - -## CONFIDENCE is the minimum confidence level from 0.0-1.0 BirdNET's analysis -## should reach before creating an entry in the BirdNET.selection.txt file. -## Don't set this to 1.0 or you won't have any results. - -CONFIDENCE=0.7 - -## SENSITIVITY is the detection sensitivity from 0.5-1.5. - -SENSITIVITY=1.25 - -## CHANNELS holds the variabel that corresponds to the number of channels the -## sound card supports. - -CHANNELS=2 - -## FULL_DISK can be set to configure how the system reacts to a full disk -## 0 = Remove the oldest day's worth of recordings -## 1 = Keep all data and 'stop_core_services.sh' - -FULL_DISK=0 - -## VENV is the virtual environment where the the BirdNET python build is found, -## i.e, VENV is the virtual environment miniforge built for BirdNET. - -VENV=/home/pi/BirdNET-Pi/birdnet - -## RECORDING_LENGTH sets the length of the recording that BirdNET-Lite will analyze. -RECORDING_LENGTH= - -## EXTRACTION_LENGTH sets the length of the audio extractions that will be made -## from each BirdNET-Lite detection. -EXTRACTION_LENGTH= - -## BIRDNET_USER should be the non-root user systemd should use to execute each -## service. - -BIRDNET_USER=pi - - -## These are just for debugging -LAST_RUN= -THIS_RUN= -EOF - [ -d /etc/birdnet ] || sudo mkdir /etc/birdnet - sudo ln -sf ${my_dir}/birdnet.conf /etc/birdnet/birdnet.conf -} -if [ ! -f ${HOME}/stage_1_complete ] ;then - stage_1 - stage_2 -else - stage_2 -fi - diff --git a/Birders_Guide_Installer_Configuration.txt b/Birders_Guide_Installer_Configuration.txt deleted file mode 100644 index 1bbf484..0000000 --- a/Birders_Guide_Installer_Configuration.txt +++ /dev/null @@ -1,59 +0,0 @@ -# Birders Guide Installer Configuration File - -################################################################################ -# Fill this out in order to install BirdNET-Lite -# Follow the instructions for each section. -################################################################################ - - -# 1. To find your latitude and longitude, you can go to https://maps.google.com -# 1a. Find the location on the map where you will be putting the BirdNET-Lite -# 1b. Right-click the location to view the latitude and longitude and click -# them to copy them to your clip-board. -# 2. Enter the latitude and longitude here. Be certain not to mix them up. -# The first number is the latitude, and the second is longitude. See the -# example below. -# -# Example: these coordinates would indicate the Eiffel Tower in Paris, France. -# LATITUDE=48.858 -# LONGITUDE=2.294 - -LATITUDE= -LONGITUDE= - -# 1. CADDY_PWD is the password you will use to access your live audio stream and -# maintenance tools. You can set this to anything, but keep it alpha-numeric. -# You will probably also want to write this one down if you ever plan to -# listen to the live stream. -# 2. ICE_PWD is the password that IceCast2 will use to authenticate the stream -# source.You can set this to anything also, but keep it alpha-numeric. You -# will never use this again, so just set it to your favorite word. - -CADDY_PWD= -ICE_PWD= - -# DB_PWD is the password that the system will use to access the database. Set -# this to anything you want. - -DB_PWD= - -# This section can be left alone. If you setup a Pushed.co -# mobile application, you can enter the App secret and secret -# key in order to enable phone notifications for new species -# detections. -PUSHED_APP_SECRET= -PUSHED_APP_KEY= - -# If you own your own domain, you can input that here to have caddy register -# TLS certificates for the web interface - -BIRDNETPI_URL= -BIRDNETLOG_URL= -EXTRACTIONLOG_URL= - -# If you would like to POST your BirdNET-Pi detections to -# https://app.birdweather.com, reach out to github:@timsterc for a unique and -# confidential BirdWeather ID. Place that here if you have one already. -# You can always add this later and the system will immediately start POSTing. - -BIRDWEATHER_ID= diff --git a/README.md b/README.md index 03ec43c..1db3c57 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- BirdNET-Pi + BirdNET-Pi

A realtime acoustic bird classification system for the Raspberry Pi 4B @@ -37,6 +37,9 @@ If your installation isn't in one of the countries listed above, please let me k * 24/7 recording and BirdNET-Lite analysis * [BirdWeather](https://app.birdweather.com) integration (you will need to be issued a BirdWeather ID -- for now, request that from [@timsterc here](https://github.com/mcguirepr89/BirdNET-Pi/discussions/82)) * Web interface access to all data and logs +* Web Terminal +* [Tiny File Manager](https://tinyfilemanager.github.io/) +* FTP server included * Automatic extraction of detected data (creating audio clips of detected bird sounds) * Spectrograms available for all extractions * MariaDB integration @@ -47,27 +50,25 @@ If your installation isn't in one of the countries listed above, please let me k * Localization supported ## Requirements -* A Raspberry Pi 4B -* An SD Card with the 64-bit version of RaspiOS installed (please use Bullseye) [(download the latest here)](https://downloads.raspberrypi.org/raspios_arm64/images/) +* A Raspberry Pi 4B or Raspberry Pi 3B[+] (must run on RaspiOS-ARM64-Lite) +* An SD Card with the 64-bit version of RaspiOS installed (please use Bullseye) -- Lite is recommended, but the installation works on RaspiOS-ARM64-Full as well. [(Download the latest here)](https://downloads.raspberrypi.org/raspios_lite_arm64/images/) * A USB Microphone or Sound Card ## Installation -Headless installation guide available [HERE](https://github.com/mcguirepr89/BirdNET-Pi/wiki/%22Headless%22-installation-using-VNC)
-Pre-installeld beta image available for testing [HERE](https://github.com/mcguirepr89/BirdNET-Pi/discussions/11#discussioncomment-1751201) +[An installation guide is available here](https://github.com/mcguirepr89/BirdNET-Pi/wiki/Installation-Guide). The system can be installed with: ``` -curl -s https://raw.githubusercontent.com/mcguirepr89/BirdNET-Pi/main/newinstaller.sh | bash +curl -s https://raw.githubusercontent.com/mcguirepr89/BirdNET-Pi/main/newinstaller.sh | bash | tee -a installation.log 2>&1 && sudo reboot ``` +The installer takes care of any and all necessary updates, so you can run that as the very first command upon the first boot, if you'd like. + +The installation creates a log in `/home/pi/installation.log` that you can [email me](mailto:mcguirepr89@gmail.com) if you encounter any issues during installation. ## Access The BirdNET-Pi system can be accessed from any web browser on the same network: - http://birdnetpi.local -#### Access Credentials: -- Username:`birdnet` -- Password: The "CADDY_PWD" password set during installation - ## Uninstallation ``` /usr/local/bin/uninstall.sh && cd ~ && rm -drf BirdNET-Pi @@ -87,7 +88,7 @@ I hope that if you find BirdNET-Pi has been worth your time, you will share your ## ToDo, Notes, and Coming Soon ### Internationalization: -The bird names are in English by default, but other localized versions are available thanks to the wonderful efforts of [@patlevin](https://github.com/patlevin). Please unzip `model/labels_l18n.zip` and replace `model/labels.txt` with your corresponding language. For instance, if you want the Swedish labels, rename the current `labels.txt` to `labels_en.txt` and then rename `labels_sv.txt` to `labels.txt`. (I will make this more straightforward in the future.) +The bird names are in English by default, but other localized versions are available thanks to the wonderful efforts of [@patlevin](https://github.com/patlevin). Use the web interface's "Tools" > "Settings" and select your "Database Language" to have the detections in your language. ### Realtime Analysis Predictions View The pre-built TFLite binaries for this project also support [the BirdNET-Demo](https://github.com/kahst/BirdNET-Demo), which I am currently testing for integration into the BirdNET-Pi. If you know anything about JavaScript and are willing to help, please let me know in the [Live Analysis discussion](https://github.com/mcguirepr89/BirdNET-Pi/discussions/24). @@ -101,7 +102,7 @@ Expect FULL internationalization options during installation (and available post - French - Spanish -and detection/database localization for the following languages: +Current database languages include the list below: | Language | Missing Species out of 6,362 | Missing labels (%) | | -------- | ------- | ------ | | Afrikaans | 5774 | 90.76% | diff --git a/analyze.py b/analyze.py deleted file mode 100644 index b90038f..0000000 --- a/analyze.py +++ /dev/null @@ -1,353 +0,0 @@ -# BirdWeather edits by @timsterc -# Other edits by @CaiusX and @mcguirepr89 -import os -os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' -os.environ['CUDA_VISIBLE_DEVICES'] = '' - -try: - import tflite_runtime.interpreter as tflite -except: - from tensorflow import lite as tflite - -import argparse -import operator -import librosa -import numpy as np -import math -import time -from decimal import Decimal -import json -############################################################################### -import requests -import mysql.connector -############################################################################### -import datetime -import pytz -from tzlocal import get_localzone -from pathlib import Path - -def loadModel(): - - global INPUT_LAYER_INDEX - global OUTPUT_LAYER_INDEX - global MDATA_INPUT_INDEX - global CLASSES - - print('LOADING TF LITE MODEL...', end=' ') - - # Load TFLite model and allocate tensors. - interpreter = tflite.Interpreter(model_path='model/BirdNET_6K_GLOBAL_MODEL.tflite',num_threads=2) - interpreter.allocate_tensors() - - # Get input and output tensors. - input_details = interpreter.get_input_details() - output_details = interpreter.get_output_details() - - # Get input tensor index - INPUT_LAYER_INDEX = input_details[0]['index'] - MDATA_INPUT_INDEX = input_details[1]['index'] - OUTPUT_LAYER_INDEX = output_details[0]['index'] - - # Load labels - CLASSES = [] - with open('model/labels.txt', 'r') as lfile: - for line in lfile.readlines(): - CLASSES.append(line.replace('\n', '')) - - print('DONE!') - - return interpreter - -def loadCustomSpeciesList(path): - - slist = [] - if os.path.isfile(path): - with open(path, 'r') as csfile: - for line in csfile.readlines(): - slist.append(line.replace('\r', '').replace('\n', '')) - - return slist - -def splitSignal(sig, rate, overlap, seconds=3.0, minlen=1.5): - - # Split signal with overlap - sig_splits = [] - for i in range(0, len(sig), int((seconds - overlap) * rate)): - split = sig[i:i + int(seconds * rate)] - - # End of signal? - if len(split) < int(minlen * rate): - break - - # Signal chunk too short? Fill with zeros. - if len(split) < int(rate * seconds): - temp = np.zeros((int(rate * seconds))) - temp[:len(split)] = split - split = temp - - sig_splits.append(split) - - return sig_splits - -def readAudioData(path, overlap, sample_rate=48000): - - print('READING AUDIO DATA...', end=' ', flush=True) - - # Open file with librosa (uses ffmpeg or libav) - sig, rate = librosa.load(path, sr=sample_rate, mono=True, res_type='kaiser_fast') - - # Split audio into 3-second chunks - chunks = splitSignal(sig, rate, overlap) - - print('DONE! READ', str(len(chunks)), 'CHUNKS.') - - return chunks - -def convertMetadata(m): - - # Convert week to cosine - if m[2] >= 1 and m[2] <= 48: - m[2] = math.cos(math.radians(m[2] * 7.5)) + 1 - else: - m[2] = -1 - - # Add binary mask - mask = np.ones((3,)) - if m[0] == -1 or m[1] == -1: - mask = np.zeros((3,)) - if m[2] == -1: - mask[2] = 0.0 - - return np.concatenate([m, mask]) - -def custom_sigmoid(x, sensitivity=1.0): - return 1 / (1.0 + np.exp(-sensitivity * x)) - -def predict(sample, interpreter, sensitivity): - - # Make a prediction - interpreter.set_tensor(INPUT_LAYER_INDEX, np.array(sample[0], dtype='float32')) - interpreter.set_tensor(MDATA_INPUT_INDEX, np.array(sample[1], dtype='float32')) - interpreter.invoke() - prediction = interpreter.get_tensor(OUTPUT_LAYER_INDEX)[0] - - # Apply custom sigmoid - p_sigmoid = custom_sigmoid(prediction, sensitivity) - - # Get label and scores for pooled predictions - p_labels = dict(zip(CLASSES, p_sigmoid)) - - # Sort by score - p_sorted = sorted(p_labels.items(), key=operator.itemgetter(1), reverse=True) - - # Remove species that are on blacklist - for i in range(min(10, len(p_sorted))): - if p_sorted[i][0] in ['Human_Human', 'Non-bird_Non-bird', 'Noise_Noise']: - p_sorted[i] = (p_sorted[i][0], 0.0) - - # Only return first the top ten results - return p_sorted[:10] - -def analyzeAudioData(chunks, lat, lon, week, sensitivity, overlap, interpreter): - - detections = {} - start = time.time() - print('ANALYZING AUDIO...', end=' ', flush=True) - - # Convert and prepare metadata - mdata = convertMetadata(np.array([lat, lon, week])) - mdata = np.expand_dims(mdata, 0) - - # Parse every chunk - pred_start = 0.0 - for c in chunks: - - # Prepare as input signal - sig = np.expand_dims(c, 0) - - # Make prediction - p = predict([sig, mdata], interpreter, sensitivity) - - # Save result and timestamp - pred_end = pred_start + 3.0 - detections[str(pred_start) + ';' + str(pred_end)] = p - pred_start = pred_end - overlap - - print('DONE! Time', int((time.time() - start) * 10) / 10.0, 'SECONDS') - - return detections - -def writeResultsToFile(detections, min_conf, path): - - print('WRITING RESULTS TO', path, '...', end=' ') - rcnt = 0 - with open(path, 'w') as rfile: - rfile.write('Start (s);End (s);Scientific name;Common name;Confidence\n') - for d in detections: - for entry in detections[d]: - if entry[1] >= min_conf and (entry[0] in WHITE_LIST or len(WHITE_LIST) == 0): - rfile.write(d + ';' + entry[0].replace('_', ';') + ';' + str(entry[1]) + '\n') - rcnt += 1 - print('DONE! WROTE', rcnt, 'RESULTS.') - -def main(): - - global WHITE_LIST - - # Parse passed arguments - parser = argparse.ArgumentParser() - parser.add_argument('--i', help='Path to input file.') - parser.add_argument('--o', default='result.csv', help='Path to output file. Defaults to result.csv.') - parser.add_argument('--lat', type=float, default=-1, help='Recording location latitude. Set -1 to ignore.') - parser.add_argument('--lon', type=float, default=-1, help='Recording location longitude. Set -1 to ignore.') - parser.add_argument('--week', type=int, default=-1, help='Week of the year when the recording was made. Values in [1, 48] (4 weeks per month). Set -1 to ignore.') - parser.add_argument('--overlap', type=float, default=0.0, help='Overlap in seconds between extracted spectrograms. Values in [0.0, 2.9]. Defaults tp 0.0.') - parser.add_argument('--sensitivity', type=float, default=1.0, help='Detection sensitivity; Higher values result in higher sensitivity. Values in [0.5, 1.5]. Defaults to 1.0.') - parser.add_argument('--min_conf', type=float, default=0.1, help='Minimum confidence threshold. Values in [0.01, 0.99]. Defaults to 0.1.') - parser.add_argument('--custom_list', default='', help='Path to text file containing a list of species. Not used if not provided.') - parser.add_argument('--birdweather_id', default='99999', help='Private Station ID for BirdWeather.') - - args = parser.parse_args() - - # Load model - interpreter = loadModel() - - # Load custom species list - if not args.custom_list == '': - WHITE_LIST = loadCustomSpeciesList(args.custom_list) - else: - WHITE_LIST = [] - - birdweather_id = args.birdweather_id - - # Read audio data - audioData = readAudioData(args.i, args.overlap) - - # Get Date/Time from filename in case Pi gets behind - #now = datetime.now() - full_file_name = args.i - file_name = Path(full_file_name).stem - file_date = file_name.split('-birdnet-')[0] - file_time = file_name.split('-birdnet-')[1] - date_time_str = file_date + ' ' + file_time - date_time_obj = datetime.datetime.strptime(date_time_str, '%Y-%m-%d %H:%M:%S') - #print('Date:', date_time_obj.date()) - #print('Time:', date_time_obj.time()) - print('Date-time:', date_time_obj) - now = date_time_obj - current_date = now.strftime("%Y/%m/%d") - current_time = now.strftime("%H:%M:%S") - current_iso8601 = now.astimezone(get_localzone()).isoformat() - - week_number = int(now.strftime("%V")) - week = max(1, min(week_number, 48)) - - sensitivity = max(0.5, min(1.0 - (args.sensitivity - 1.0), 1.5)) - - # Process audio data and get detections - detections = analyzeAudioData(audioData, args.lat, args.lon, week, sensitivity, args.overlap, interpreter) - - # Write detections to output file - min_conf = max(0.01, min(args.min_conf, 0.99)) - writeResultsToFile(detections, min_conf, args.o) - -############################################################################### -############################################################################### - - soundscape_uploaded = False - - # Write detections to Database - for i in detections: - print("\n", detections[i][0],"\n") - with open('BirdDB.txt', 'a') as rfile: - for d in detections: - print("\n", "Database Entry", "\n") - for entry in detections[d]: - if entry[1] >= min_conf and (entry[0] in WHITE_LIST or len(WHITE_LIST) == 0): - rfile.write(str(current_date) + ';' + str(current_time) + ';' + entry[0].replace('_', ';') + ';' \ - + str(entry[1]) +";" + str(args.lat) + ';' + str(args.lon) + ';' + str(min_conf) + ';' + str(week) + ';' \ - + str(sensitivity) +';' + str(args.overlap) + '\n') - - def insert_variables_into_table(Date, Time, Sci_Name, Com_Name, Confidence, Lat, Lon, Cutoff, Week, Sens, Overlap): - try: - connection = mysql.connector.connect(host='localhost', - database='birds', - user='birder', - password='databasepassword') - cursor = connection.cursor() - mySql_insert_query = """INSERT INTO detections (Date, Time, Sci_Name, Com_Name, Confidence, Lat, Lon, Cutoff, Week, Sens, Overlap) - VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) """ - - record = (Date, Time, Sci_Name, Com_Name, Confidence, Lat, Lon, Cutoff, Week, Sens, Overlap) - - cursor.execute(mySql_insert_query, record) - connection.commit() - print("Record inserted successfully into detections table") - - - except mysql.connector.Error as error: - print("Failed to insert record into detections table {}".format(error)) - - finally: - if connection.is_connected(): - connection.close() - print("MySQL connection is closed") - - species = entry[0] - sci_name,com_name = species.split('_') - insert_variables_into_table(str(current_date), str(current_time), sci_name, com_name, \ - str(entry[1]), str(args.lat), str(args.lon), str(min_conf), str(week), \ - str(args.sensitivity), str(args.overlap)) - - print(str(current_date) + ';' + str(current_time) + ';' + entry[0].replace('_', ';') + ';' + str(entry[1]) +";" + str(args.lat) + ';' + str(args.lon) + ';' + str(min_conf) + ';' + str(week) + ';' + str(args.sensitivity) +';' + str(args.overlap) + '\n') - - if birdweather_id != "99999": - - if soundscape_uploaded is False: - # POST soundscape to server - soundscape_url = "https://app.birdweather.com/api/v1/stations/" + birdweather_id + "/soundscapes" + "?timestamp=" + current_iso8601 - - with open(args.i, 'rb') as f: - wav_data = f.read() - response = requests.post(url=soundscape_url, data=wav_data, headers={'Content-Type': 'application/octet-stream'}) - print("Soundscape POST Response Status - ", response.status_code) - sdata = response.json() - soundscape_id = sdata['soundscape']['id'] - soundscape_uploaded = True - - # POST detection to server - detection_url = "https://app.birdweather.com/api/v1/stations/" + birdweather_id + "/detections" - start_time = d.split(';')[0] - end_time = d.split(';')[1] - post_begin = "{ " - now_p_start = now + datetime.timedelta(seconds=float(start_time)) - current_iso8601 = now_p_start.astimezone(get_localzone()).isoformat() - post_timestamp = "\"timestamp\": \"" + current_iso8601 + "\"," - post_lat = "\"lat\": " + str(args.lat) + "," - post_lon = "\"lon\": " + str(args.lon) + "," - post_soundscape_id = "\"soundscapeId\": " + str(soundscape_id) + "," - post_soundscape_start_time = "\"soundscapeStartTime\": " + start_time + "," - post_soundscape_end_time = "\"soundscapeEndTime\": " + end_time + "," - post_commonName = "\"commonName\": \"" + entry[0].split('_')[1] + "\"," - post_scientificName = "\"scientificName\": \"" + entry[0].split('_')[0] + "\"," - post_algorithm = "\"algorithm\": " + "\"alpha\"" + "," - post_confidence = "\"confidence\": " + str(entry[1]) - post_end = " }" - - post_json = post_begin + post_timestamp + post_lat + post_lon + post_soundscape_id + post_soundscape_start_time + post_soundscape_end_time + post_commonName + post_scientificName + post_algorithm + post_confidence + post_end - print(post_json) - response = requests.post(detection_url, json=json.loads(post_json)) - print("Detection POST Response Status - ", response.status_code) - - #time.sleep(3) - -############################################################################### -############################################################################### - -if __name__ == '__main__': - - main() - - # Example calls - # python3 analyze.py --i 'example/XC558716 - Soundscape.mp3' --lat 35.4244 --lon -120.7463 --week 18 - # python3 analyze.py --i 'example/XC563936 - Soundscape.mp3' --lat 47.6766 --lon -122.294 --week 11 --overlap 1.5 --min_conf 0.25 --sensitivity 1.25 --custom_list 'example/custom_species_list.txt' diff --git a/birdnet.conf-defaults b/birdnet.conf-defaults index 57a2c99..c84d4ae 100644 --- a/birdnet.conf-defaults +++ b/birdnet.conf-defaults @@ -2,33 +2,26 @@ # Configuration settings for BirdNET-Pi # ################################################################################ -############ CHANGE THE LATITUDE AND LONGITUDE TO STATIC VALUES ################ +#--------------------- Required: Latitude, and Longitude ----------------------# +## The shell substitution below guesses these based on your network. THESE NEED +## TO BE CHANGED TO STATIC VALUES +## Please only go to 4 decimal places. Example:43.3984 -## LATITUDE and LONGITUDE are self-explanatroy. Find them easily at -## maps.google.com. -## Example: these coordinates would indicate the Eiffel Tower in Paris, France. -## LATITUDE=48.858 -## LONGITUDE=2.294 - -## These are input as shell substitutions so that this will work out of the box. -## It guesses your latitude and longitude based off of your network information. -## THESE SHOULD BE CHANGED TO STATIC NUMBERS!!!! LATITUDE="$(curl -s4 ifconfig.co/json | awk '/lat/ {print $2}' | tr -d ',')" LONGITUDE="$(curl -s4 ifconfig.co/json | awk '/lon/ {print $2}' | tr -d ',')" -## RECS_DIR is the location birdnet_analysis.service will look for the data-set -## it needs to analyze. Be sure this directory is readable and writable for -## the BIRDNET_USER. If you are going to be accessing a remote data-set, you -## still need to set this, as this will be where the remote directory gets -## mounted locally. -RECS_DIR=/home/pi/BirdSongs +#--------------------- BirdWeather Station Information -----------------------# +#_____________The variable below can be set to have your BirdNET-Pi____________# +#__________________also act as a BirdWeather listening station_________________# + +BIRDWEATHER_ID= #----------------------- Web Interface User Password ------------------------# #____________________The variable below sets the 'birdnet'_____________________# -#___________________user password for the live audio stream,___________________# -#_________________web tools, system info, and processed files__________________# +#___________________user password for the Live Audio Stream,___________________# +#_________________Tools, System Links, and the Processed files ________________# ## CADDY_PWD is the plaintext password (that will be hashed) and used to access ## certain parts of the web interface @@ -39,7 +32,8 @@ CADDY_PWD= #_____________The variable below sets the password for the_____________________# #_______________________'birder' user on the MariaDB___________________________# -## DB_PWD is for the 'birder' user +## DB_PWD is for the 'birder' user. The shell substitution below sets a random +## password for you during installation. DB_PWD=$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c${1:-32};echo;) #------------------------- Live Audio Stream --------------------------------# @@ -49,16 +43,10 @@ DB_PWD=$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c${1:-32};echo;) ## ICE_PWD is the password that icecast2 will use to authenticate ffmpeg as a ## trusted source for the stream. You will never need to enter this manually -## anywhere other than here. +## anywhere other than here and it stays on 'localhost.' ICE_PWD=birdnetpi -#--------------------- BirdWeather Station Information -----------------------# -#_____________The variable below can be set to have your BirdNET-Pi____________# -#__________________also act as a BirdWeather listening station_________________# - -BIRDWEATHER_ID= - #----------------------- Web-hosting/Caddy File-server -----------------------# #________The four variables below can be set to enable internet access_________# #____________to your data,(e.g., extractions, raw data, live___________________# @@ -70,8 +58,8 @@ BIRDWEATHER_ID= ## the BirdNET-Pi on your local network, keep this EMPTY. BIRDNETPI_URL= -EXTRACTIONLOG_URL= BIRDNETLOG_URL= +WEBTERMINAL_URL= #------------------- Mobile Notifications via Pushed.co ---------------------# @@ -90,19 +78,11 @@ PUSHED_APP_SECRET= #-------------------------------- Defaults ----------------------------------# ################################################################################ -#------------------------------- NoMachine ----------------------------------# -#_____________The variable below can be set include NoMachine__________________# -#_________________remote desktop software to be installed._____________________# +## RECS_DIR is the location birdnet_analysis.service will look for the data-set +## it needs to analyze. Be sure this directory is readable and writable for +## the BIRDNET_USER. -# Keep this EMPTY if you do not want to install NoMachine. # - -## INSTALL_NOMACHINE is simply a setting that can be enabled to install -## NoMachine alongside the BirdNET-Pi for remote desktop access. This in- -## staller assumes personal use. Please reference the LICENSE file included -## in this repository for more information. -## Set this to Y or y to install NoMachine alongside the BirdNET-Lite - -INSTALL_NOMACHINE=y +RECS_DIR=/home/pi/BirdSongs # #------------------------------ Extraction Service ---------------------------# @@ -176,10 +156,10 @@ SENSITIVITY=1.25 CHANNELS=2 ## FULL_DISK can be set to configure how the system reacts to a full disk -## 0 = Remove the oldest day's worth of recordings -## 1 = Keep all data and `stop_core_services.sh` +## purge = Remove the oldest day's worth of recordings +## keep = Keep all data and 'stop_core_services.sh' -FULL_DISK=0 +FULL_DISK=purge ## VENV is the virtual environment where the the BirdNET python build is found, ## i.e, VENV is the virtual environment miniforge built for BirdNET. @@ -187,7 +167,7 @@ FULL_DISK=0 VENV=/home/pi/BirdNET-Pi/birdnet ## RECORDING_LENGTH sets the length of the recording that BirdNET-Lite will analyze. -RECORDING_LENGTH= +RECORDING_LENGTH=15 ## EXTRACTION_LENGTH sets the length of the audio extractions that will be made ## from each BirdNET-Lite detection. @@ -202,3 +182,18 @@ BIRDNET_USER=pi ## These are just for debugging LAST_RUN= THIS_RUN= + +## DEPRECATED +#------------------------------- NoMachine ----------------------------------# +#_____________The variable below can be set include NoMachine__________________# +#_________________remote desktop software to be installed._____________________# + +# Keep this EMPTY if you do not want to install NoMachine. # + +## INSTALL_NOMACHINE is simply a setting that can be enabled to install +## NoMachine alongside the BirdNET-Pi for remote desktop access. This in- +## staller assumes personal use. Please reference the LICENSE file included +## in this repository for more information. +## Set this to Y or y to install NoMachine alongside the BirdNET-Lite + +INSTALL_NOMACHINE=y diff --git a/homepage/form.html b/homepage/form.html deleted file mode 100644 index 2b3d2a6..0000000 --- a/homepage/form.html +++ /dev/null @@ -1,84 +0,0 @@ - - - Configure `birdnet.conf` - - -

Configure BirdNET-Pi

- -
-

Required

- -
- -
- -
- -
- -
- -
-
-

Optional Services

- -
- -
- -
- -
-
-

Custom URLs

- -
- -
- -
- -
-
-

Default Services

- -
- -
- -
- -
-
-

Advanced Configuration

- -
- -
- -
- -
- -
- -
- -
- -
- View Current Config - - diff --git a/homepage/form.php b/homepage/form.php deleted file mode 100644 index 534b525..0000000 --- a/homepage/form.php +++ /dev/null @@ -1,24 +0,0 @@ -"; -echo "File:
"; -$file = file("scripts/birdnet.conf"); -foreach($file as $text) { -echo $text."
"; -} -}else{ -$file = file("scripts/birdnet.conf"); -echo "
"; -echo ""; -echo "\n -
"; -} -?> diff --git a/homepage/menu.html b/homepage/menu.html index 947715c..3e2b3af 100644 --- a/homepage/menu.html +++ b/homepage/menu.html @@ -21,7 +21,7 @@ h3 { text-align: center; } h4,h5 { - text-align: center; + text-align: left; margin: 0px 0px 5px 0px; } footer { @@ -43,34 +43,33 @@ body::-webkit-scrollbar {
Extractions
- - By Date
- - By Common Name
- - By Scientific Name
- - Processed
+ - By Date
+ - By Common Name
+ - By Scientific Name
+ - Processed

-
System Links
- - Tools
- - System Info
- - BirdNET-Lite Log
- - Extraction Log
+
System
+ - Tools
+ - Info
+ - Log

-
External Links
+
External
- BirdWeather
- Repository
- Wiki Help Page
- Wttr.in Report
- - BirdNET @ Cornell
+ - BirdNET

Other
BirdNET-Pis
- - United States
- - South Africa
- - Sweden
- - Germany + - United States
+ - South Africa
+ - Sweden
+ - Germany