diff --git a/src/aprs_pico.c b/src/aprs_pico.c
index f8b643a..f88d55d 100644
--- a/src/aprs_pico.c
+++ b/src/aprs_pico.c
@@ -1,4 +1,208 @@
+/*
+* Project 'raspi-pico-aprs-tnc'
+* Copyright (C) 2021 Thomas Glau, DL3TG
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*/
+
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+
+
+// Do not change: ATTOW, the pico audio lib worked only @ 22050 Hz sampling frequency
+#define SAMPLE_FREQ_IN_HZ (22050)
+#define SEND_LOOP_DEBUG (1) // For debug
+
+
+
+audio_buffer_pool_t* init_audio(uint sample_freq_in_hz, uint16_t audio_buffer_format)
+{
+ const int NUM_AUDIO_BUFFERS = 3;
+ const int SAMPLES_PER_BUFFER = 256;
+
+ const audio_format_t audio_format = {.format = audio_buffer_format,
+ .sample_freq = sample_freq_in_hz,
+ .channel_count = 1};
+
+ audio_buffer_format_t producer_format = {.format = &audio_format,
+ .sample_stride = 2};
+
+ audio_buffer_pool_t* producer_pool = audio_new_producer_pool(&producer_format, NUM_AUDIO_BUFFERS, SAMPLES_PER_BUFFER);
+
+ if (!audio_pwm_setup(&audio_format, -1, &default_mono_channel_config))
+ {
+ panic("PicoAudio: Unable to open audio device.\n");
+ }
+
+ bool __unused is_ok = audio_pwm_default_connect(producer_pool, false);
+ assert(is_ok);
+
+ audio_pwm_set_enabled(true);
+
+ return producer_pool;
+}
+
+
+static void init(uint sample_freq_in_hz)
+{
+ // NOTE: ATTOW, the pico audio lib worked only @ 22050 Hz sampling frequency,
+ // related to 48 MHz system clock
+
+ const uint SAMPLING_FREQ_REF_IN_HZ = 22050;
+ const uint SYSTEM_CLOCK_REF_IN_KHZ = 48000;
+
+ if (sample_freq_in_hz == SAMPLING_FREQ_REF_IN_HZ)
+ {
+ set_sys_clock_48mhz();
+ }
+ else
+ {
+ // Compensate a non-reference sampling frequency by a different system clock frequency
+ // To be improved: System clock may be imprecise
+
+ // set_sys_clock_khz((uint32_t)((float)SYSTEM_CLOCK_REF_IN_KHZ * (float)sample_freq_in_hz / (float)SAMPLING_FREQ_REF_IN_HZ), false);
+ set_sys_clock_khz(105000, false); // HACK
+ }
+
+ stdio_init_all();
+}
+
+
+static void fill_audio_buffer(audio_buffer_pool_t* audio_pool, const int16_t* pcm_data, uint num_samples)
+{
+ uint pos = 0u;
+ const uint8_t volume = 128u;
+
+ while (true)
+ {
+ audio_buffer_t* buffer = take_audio_buffer(audio_pool, true);
+ int16_t* samples = (int16_t*)buffer->buffer->bytes;
+
+ for (uint i = 0u; i < buffer->max_sample_count; i++)
+ {
+ samples[i] = (volume * pcm_data[pos]) >> 8u;
+ pos++;
+
+ if (pos == num_samples)
+ {
+ pos = 0u;
+ }
+ }
+
+ buffer->sample_count = buffer->max_sample_count;
+ give_audio_buffer(audio_pool, buffer);
+ }
+}
+
+
+// Test tone: 1 kHz sine wave
+static void send1kHz(uint sample_freq_in_hz)
+{
+ init(sample_freq_in_hz);
+
+ const uint tone_freq_in_hz = 1000u;
+ const uint num_samples = sample_freq_in_hz / tone_freq_in_hz;
+
+ int16_t* sine_wave_table = malloc(num_samples * sizeof(int16_t));
+
+ if (!sine_wave_table)
+ {
+ panic("Out of memory: malloc() failed.\n");
+ }
+
+ for (uint i = 0u; i < num_samples; i++)
+ {
+ sine_wave_table[i] = 32767 * sinf(2.0f * (float)M_PI * (float)i / (float)num_samples);
+ }
+
+ audio_buffer_pool_t* audio_pool = init_audio(sample_freq_in_hz, AUDIO_BUFFER_FORMAT_PCM_S16);
+
+ while (true)
+ {
+ fill_audio_buffer(audio_pool, sine_wave_table, num_samples);
+ }
+
+ free(sine_wave_table);
+}
+
+
+static void sendAPRS_audioCallback(void* user_data, int16_t* pcm_data, size_t num_samples)
+{
+ const uint aprs_sampl_freq_in_hz = *((const uint*)user_data);
+
+ audio_buffer_pool_t* audio_pool = init_audio(aprs_sampl_freq_in_hz, AUDIO_BUFFER_FORMAT_PCM_S16);
+
+#if (SEND_LOOP_DEBUG == 1)
+ while (true)
+ {
+#endif // SEND_LOOP_DEBUG
+
+ fill_audio_buffer(audio_pool, pcm_data, num_samples);
+
+#if (SEND_LOOP_DEBUG == 1)
+ }
+#endif // SEND_LOOP_DEBUG
+}
+
+
+static void sendAPRS(const char* call_sign_src,
+ const char* call_sign_dst,
+ const char* aprs_path_1,
+ const char* aprs_path_2,
+ const char* aprs_message,
+ const double latitude_in_deg,
+ const double longitude_in_deg,
+ const double altitude_in_m)
+{
+ static const uint APRS_SAMPL_FREQ_IN_HZ = 48000u;
+
+ init(APRS_SAMPL_FREQ_IN_HZ);
+
+ ax25_beacon((void*)&APRS_SAMPL_FREQ_IN_HZ, // User data
+ sendAPRS_audioCallback,
+ call_sign_src,
+ call_sign_dst,
+ aprs_path_1,
+ aprs_path_2,
+ latitude_in_deg,
+ longitude_in_deg,
+ altitude_in_m,
+ aprs_message,
+ '/', 'O');
+}
+
+
int main()
{
+ // send1kHz(SAMPLE_FREQ_IN_HZ); // For test & debug
+
+ // Send an APRS test message
+ sendAPRS("SRC", // Src call sign
+ "DST", // Dst call sign
+ "PATH1",
+ "PATH2",
+ "Test message",
+ 10.0, // Lat in deg
+ 20.0, // Long in deg
+ 100.0 // Alt in m
+ );
+
return 0;
}