Skip to content

Naprawa zwalniania PTT, odtwarzania sampli, kontenera i rotacji logów - #7

Open
dkoryto wants to merge 5 commits into
sq2ips:masterfrom
dkoryto:dev
Open

Naprawa zwalniania PTT, odtwarzania sampli, kontenera i rotacji logów#7
dkoryto wants to merge 5 commits into
sq2ips:masterfrom
dkoryto:dev

Conversation

@dkoryto

@dkoryto dkoryto commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Pięć poprawek błędów znalezionych przy przeglądzie projektu. Bez nowych
funkcji, bez zmian w modułach pobierających dane.

Zwolnienie PTT i pętla odtwarzania (de14371)

Pętla odtwarzania nie miała żadnego zabezpieczenia, więc wyjątek pomiędzy
włączeniem a wyłączeniem PTT zostawiał zakluczony nadajnik. RPi.GPIO nie
resetuje pinów przy wyjściu z procesu, więc pin zostawał w stanie HIGH aż do
reboota — czyli zablokowana częstotliwość i emisja bez nadzoru.

  • zwalnianie PTT wydzielone do ptt_off(), idempotentnego, obsługującego
    zarówno GPIO jak i port szeregowy
  • rejestracja przez atexit oraz handlery SIGTERM/SIGINT/SIGHUP
  • pętla odtwarzania w try/finally, które zawsze je wywołuje

Sama pętla mogła rzucić wyjątkiem na kilka sposobów. voice_channel nie było
resetowane między iteracjami, więc nieudane play() albo rzucało NameError
na pierwszym sample, albo po cichu czekało na poprzedni, już zakończony kanał
— sampel wypadał z komunikatu bez żadnego śladu w logach. Puste except
łykało też KeyboardInterrupt, przez co Ctrl+C nie przerywał odtwarzania.

  • voice_channel resetowane na starcie każdej iteracji
  • łapane konkretnie KeyError i pygame.error, z logiem
  • sndarray.make_sound() objęty tym samym zabezpieczeniem

Kontener (17bfcf8)

Obraz w ogóle się nie budował: .dockerignore wykluczał .env i .git,
a Dockerfile kopiował oba, więc COPY padał na brakującym źródle. Na
świeżym klonie .env i tak nie istnieje, bo jest w .gitignore.

  • bez COPY .env — sekrety wchodzą przez --env-file, nie zostają czytelne
    w warstwie obrazu
  • bez COPY .git (108 MB) i bez pakietu git, były tam tylko dla
    sprawdzania aktualizacji
  • apt-get z -y i --no-install-recommends, usunięty apt-get upgrade
    bez potwierdzenia, który przerywa build nieinteraktywny
  • zależności instalowane przed skopiowaniem kodu, zmiana modułu nie
    przebudowuje warstwy z pip install
  • użytkownik budowany z UID/GID hosta, inaczej montowany katalog logs
    i gniazdo PulseAudio są niezapisywalne
  • ENTRYPOINT zamiast CMD, argumenty przechodzą do sr0wx.py

run-docker.sh montował cookie PulseAudio do /root, podczas gdy kontener
chodzi jako sr0wx, i nie przekazywał żadnych urządzeń — PTT nigdy nie
kluczował, po cichu.

Oba pliki konfiguracyjne odmawiały startu bez pliku .env, co czyniło
--env-file bezużytecznym. Teraz wczytują .env, gdy jest, w przeciwnym
razie biorą wartości ze środowiska, i mówią których zmiennych brakuje.
Przy okazji znika AttributeError na nieustawionym METEOSTATION_IP.

Rotacja logów (b550477)

Nazwa pliku logu zawierała znacznik czasu wyliczany przy imporcie, więc każde
uruchomienie otwierało nowy plik i RotatingFileHandler nie miał czego
rotować. Stacja chodząca co godzinę zostawiała ~8800 plików rocznie na karcie.

  • TimedRotatingFileHandler ze stałą nazwą, rotacja o północy, 30 dni
  • kodowanie utf-8, komunikaty są po polsku
  • katalog logs tworzony przy imporcie, na świeżym klonie go nie ma
  • .gitignore łapie też pliki rotowane, logs/*.log nie obejmowało
    sr0wx.log.RRRR-MM-DD

setup.sh (4550111)

Ponowne uruchomienie skryptu w celu naprawy lub aktualizacji środowiska
nadpisywało .env pustym szablonem, kasując klucze API i passcode APRS.

  • istniejący .env zostaje nietknięty
  • -r instaluje requirements-rpi.txt (dokłada RPi.GPIO)
  • cd do katalogu skryptu

Sample (798e1e4)

Regeneracja sampli liter I, W, Z. Ten sam format (Ogg Vorbis, mono, 22050 Hz),
krótsze nagrania.


Uwaga przy wdrożeniu

  • Nazwy plików logów się zmieniły: logs/sr0wx.log zamiast
    logs/RRRR-MM-DD_GG:MM.log. Jeśli coś zewnętrznego czyta logi po nazwie,
    wymaga aktualizacji.
  • W kontenerze nie ma już .git, więc sprawdzanie aktualizacji zaloguje jedną
    linię błędu na uruchomienie i poleci dalej. Do wyciszenia przez
    check_for_updates = False.
  • Nowy run-docker.sh wymaga istniejącego .env i przekazuje argumenty
    do sr0wx.py, np. ./run-docker.sh -t -m sun_rise_set.

Weryfikacja

Kompilacja wszystkich plików .py (core, moduły, skrypty, generator).
Uruchomienie core w trybie -t na module offline, a następnie na dziewięciu
modułach pobierających dane — siedem zwróciło dane, zadziałał mechanizm
modułów awaryjnych. Ścieżka numpy + pygame.sndarray (generowanie CTCSS)
sprawdzona osobno, bo tryb testowy jej nie dotyka. Rotacja logów sprawdzona
na dwóch kolejnych uruchomieniach: jeden plik, dopisywanie zamiast tworzenia
nowego. Obie gałęzie warunku na .env w setup.sh sprawdzone.

Nie zweryfikowane: docker build (brak działającego demona na maszynie, na
której to powstawało) oraz faktyczne odtwarzanie audio i kluczowanie PTT
(brak sprzętu, tryb -t kończy się przed tą sekcją). Przed wdrożeniem na
stacji warto puścić python3 sr0wx.py -m sun_rise_set bez -t i sprawdzić
raspi-gpio get 40 po zakończeniu oraz po przerwaniu Ctrl+C.

The playback loop had no cleanup guard, so any exception between switching
PTT on and off left the transmitter keyed. RPi.GPIO does not reset pins on
interpreter exit, so the pin stayed HIGH until reboot.

- extract PTT release into ptt_off(), idempotent, covering serial and GPIO
- register it via atexit and SIGTERM/SIGINT/SIGHUP handlers
- wrap the playback loop in try/finally that always calls it

The loop itself could raise in several ways. voice_channel was not reset
between iterations, so a failed play() either raised NameError on the first
sample or silently reused the previous, already finished channel, dropping
the sample without a trace. The bare except also swallowed KeyboardInterrupt,
making Ctrl+C unable to stop playback.

- reset voice_channel at the start of every iteration and skip the wait loop
  when playback failed
- catch KeyError and pygame.error instead of everything, and log the failure
- cover sndarray.make_sound() with the same handler
The image could not be built at all: .dockerignore excluded .env and .git
while the Dockerfile copied both, so COPY failed on a missing source. On a
fresh clone .env does not exist either, since it is gitignored.

- drop COPY of .env, secrets go in at runtime via --env-file, they no longer
  end up readable in an image layer
- drop COPY of .git (108 MB) and the git package, they were only there for
  the update check
- add -y and --no-install-recommends to apt-get, drop the bare apt-get
  upgrade that aborts on the confirmation prompt in a non-interactive build
- clean up apt lists, install requirements before copying the code so a code
  change does not rebuild the dependency layer
- build the user with the host UID/GID, otherwise the mounted logs directory
  and the PulseAudio socket are not writable
- switch to ENTRYPOINT so arguments can be passed through to sr0wx.py

run-docker.sh mounted the PulseAudio cookie into /root, while the container
runs as sr0wx, and passed no devices, so PTT silently never keyed.

- mount the cookie in the container user's home
- pass /dev/gpiomem and, via SR0WX_SERIAL, the serial device
- require .env, init the pyliczba submodule, persist logs, forward arguments
- skip -it when there is no terminal, so cron and systemd runs work

Both config files refused to start without a .env file, which made
--env-file useless. They now load .env when it is there, fall back to the
environment, and report which variables are actually missing. This also
fixes METEOSTATION_IP crashing with AttributeError when unset.
The log filename carried a timestamp evaluated at import time, so every run
opened a new file and RotatingFileHandler never had anything to rotate. A
station running hourly left ~8800 files a year on the SD card.

- switch both configs to TimedRotatingFileHandler with a fixed filename,
  rotating at midnight and keeping 30 days
- set encoding to utf-8, the messages contain Polish text
- create the logs directory on import, it does not exist in a fresh clone
- ignore rotated files too, logs/*.log missed sr0wx.log.YYYY-MM-DD
- drop the now unused datetime import
…ments

Rerunning the script to repair or update the virtualenv overwrote .env with
the empty example, wiping the API keys and the APRS passcode.

- keep an existing .env, only create it when it is missing
- accept -r to install requirements-rpi.txt, which additionally pulls RPi.GPIO
- cd to the script directory, so the virtualenv is not created wherever the
  script happened to be called from
Shorter recordings, same format (Ogg Vorbis, mono, 22050 Hz):
I 0.53 s -> 0.26 s, W 0.54 s -> 0.34 s, Z 0.61 s -> 0.37 s
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant