Files @ 2e9e37796ebc
Branch filter:

Location: FeatherHAB/wsprhab/src/main.c - annotation

Ethan Zonca
Switch to newlib nano. Untested on actual hardware. Saves flash/ram.
4202475a7575
4202475a7575
4202475a7575
4202475a7575
0d9900312165
0d9900312165
c1b1dfc6c2f4
0d9900312165
c1b1dfc6c2f4
0d9900312165
a127e9133034
bb703e19f242
0d9900312165
30ab6b66325f
0d9900312165
23ecc15c694d
659774b354b1
f40195400941
659774b354b1
659774b354b1
ddbcfaffc98a
ddbcfaffc98a
ddbcfaffc98a
ddbcfaffc98a
ddbcfaffc98a
ddbcfaffc98a
ddbcfaffc98a
659774b354b1
659774b354b1
b32b9376de92
659774b354b1
659774b354b1
50cc79d449a4
659774b354b1
f126c64d1202
c396707da341
d4a53aacce1c
736f2e74e445
736f2e74e445
f126c64d1202
659774b354b1
0d9900312165
0d9900312165
0d9900312165
f40195400941
0d9900312165
0d9900312165
af5348c12bda
bb703e19f242
4202475a7575
659774b354b1
23ecc15c694d
0d9900312165
21759af9d2ad
21759af9d2ad
4ffdc5eab0ff
659774b354b1
f2f8cb2bebfa
7f3f8f2d539f
7f3f8f2d539f
876e5a9f77ab
4ffdc5eab0ff
0b86288f0749
c396707da341
c396707da341
dda4d4df3034
8bad90087cda
7f3f8f2d539f
0b86288f0749
f40195400941
736f2e74e445
30ab6b66325f
30ab6b66325f
30ab6b66325f
30ab6b66325f
30ab6b66325f
30ab6b66325f
f2f8cb2bebfa
f2f8cb2bebfa
cc569dddb24e
c396707da341
c396707da341
c396707da341
0d9900312165
0d9900312165
b32b9376de92
c396707da341
af5348c12bda
af5348c12bda
af5348c12bda
b32b9376de92
30ab6b66325f
c396707da341
677c51754ccf
c396707da341
c396707da341
9ba4d6855ba4
b32b9376de92
876e5a9f77ab
876e5a9f77ab
876e5a9f77ab
876e5a9f77ab
876e5a9f77ab
b32b9376de92
b32b9376de92
b32b9376de92
b32b9376de92
b32b9376de92
f40195400941
c396707da341
dda4d4df3034
b32b9376de92
b32b9376de92
b32b9376de92
b32b9376de92
b32b9376de92
b32b9376de92
c396707da341
dda4d4df3034
b32b9376de92
b32b9376de92
677c51754ccf
677c51754ccf
677c51754ccf
677c51754ccf
659774b354b1
f76c13fc8299
659774b354b1
659774b354b1
659774b354b1
7f3f8f2d539f
7f3f8f2d539f
ddbcfaffc98a
f2f8cb2bebfa
7f3f8f2d539f
f2f8cb2bebfa
f2f8cb2bebfa
f2f8cb2bebfa
f2f8cb2bebfa
7f3f8f2d539f
f2f8cb2bebfa
ddbcfaffc98a
d4a53aacce1c
d4a53aacce1c
f2f8cb2bebfa
d4a53aacce1c
ddbcfaffc98a
659774b354b1
659774b354b1
659774b354b1
659774b354b1
659774b354b1
e1db5d4a8be4
876e5a9f77ab
876e5a9f77ab
659774b354b1
9ba4d6855ba4
0b86288f0749
c396707da341
9ba4d6855ba4
0b86288f0749
659774b354b1
30ab6b66325f
30ab6b66325f
30ab6b66325f
bb703e19f242
dda4d4df3034
659774b354b1
659774b354b1
9ba4d6855ba4
659774b354b1
659774b354b1
659774b354b1
659774b354b1
b295af18d965
659774b354b1
0b86288f0749
659774b354b1
af6b7df096c5
659774b354b1
bb703e19f242
c396707da341
0b86288f0749
bb703e19f242
0b86288f0749
bb703e19f242
38f0f2457409
c396707da341
bb703e19f242
0b86288f0749
876e5a9f77ab
876e5a9f77ab
876e5a9f77ab
876e5a9f77ab
876e5a9f77ab
876e5a9f77ab
876e5a9f77ab
659774b354b1
a127e9133034
f2f8cb2bebfa
659774b354b1
659774b354b1
659774b354b1
876e5a9f77ab
876e5a9f77ab
876e5a9f77ab
b32b9376de92
b32b9376de92
c396707da341
b32b9376de92
c396707da341
b32b9376de92
f126c64d1202
f126c64d1202
f126c64d1202
f126c64d1202
f126c64d1202
f126c64d1202
f40195400941
f40195400941
8e3cff0b603c
c396707da341
b32b9376de92
af6b7df096c5
b32b9376de92
b32b9376de92
b32b9376de92
b32b9376de92
c396707da341
b32b9376de92
af6b7df096c5
b32b9376de92
dda4d4df3034
b32b9376de92
fb8415e8edb9
fb8415e8edb9
876e5a9f77ab
876e5a9f77ab
876e5a9f77ab
876e5a9f77ab
876e5a9f77ab
876e5a9f77ab
876e5a9f77ab
876e5a9f77ab
876e5a9f77ab
876e5a9f77ab
fb8415e8edb9
21759af9d2ad
659774b354b1
659774b354b1
f76c13fc8299
aa624684a65e
30ab6b66325f
f2f8cb2bebfa
30ab6b66325f
c396707da341
30ab6b66325f
30ab6b66325f
659774b354b1
f2f8cb2bebfa
30ab6b66325f
30ab6b66325f
30ab6b66325f
30ab6b66325f
30ab6b66325f
736f2e74e445
0d9900312165
0d9900312165
0d9900312165
7f3f8f2d539f
d4a53aacce1c
d4a53aacce1c
d4a53aacce1c
876e5a9f77ab
d4a53aacce1c
d4a53aacce1c
d4a53aacce1c
d4a53aacce1c
d4a53aacce1c
d4a53aacce1c
d4a53aacce1c
d4a53aacce1c
d4a53aacce1c
d4a53aacce1c
d4a53aacce1c
d4a53aacce1c
d4a53aacce1c
d4a53aacce1c
d4a53aacce1c
7f3f8f2d539f
c396707da341
736f2e74e445
736f2e74e445
736f2e74e445
736f2e74e445
0d9900312165
f126c64d1202
f126c64d1202
f126c64d1202
f126c64d1202
f126c64d1202
f126c64d1202
f126c64d1202
f126c64d1202
f126c64d1202
f126c64d1202
f126c64d1202
f126c64d1202
4ffdc5eab0ff
f126c64d1202
f126c64d1202
f126c64d1202
f126c64d1202
f126c64d1202
f126c64d1202
f126c64d1202
f126c64d1202
f126c64d1202
f126c64d1202
f126c64d1202
f126c64d1202
f126c64d1202
f126c64d1202
f126c64d1202
4ffdc5eab0ff
//
// WSPRHAB: Minimal high-altitude balloon tracker with WSPR telemetry
//

#include "stm32f0xx_hal.h"
#include "adc.h"
#include "system.h"
#include "i2c.h"
#include "uart.h"
#include "gpio.h"
#include "wspr.h"
#include "rtc.h"
#include "gps.h"
#include "config.h"


// We have access to the 1PPS pin of the gps... could have trim routine for internal oscillator based on this when we have a fix
// Probable wake up 1 minute early -- 0.45min possible +/- on wakeup time with 15min sync intervals


// TODO: Add JT9 message with more grid locator digits + altitude + vbatt + temp
// MSG13charmax:
// 	X: gridloc
//  Y: altitude
//  Z: temperature
//  KD8TDF XXYYZZ // could use alt callsign thing

enum _state
{
    SYSTEM_IDLE = 0, // awaiting RTC interrupt for wakeup TODO wake up before scheduled time to get fix?
    SYSTEM_GPSACQ, // RTC interrupted
    SYSTEM_WSPRTX, // Wait for timeslot and actually transmit the message
};

static void __calc_gridloc(char *dst, double lat, double lon);
static void __ledpulse(void);
static void __sleep_enter_stop(void);

uint32_t statled_ontime = 0;


int main(void)
{
    HAL_Init();
    HAL_Delay(1000); // startup delay before infinisleep

    sysclk_init();
    gpio_init();
    rtc_init();
    adc_init();
    wspr_init();

    uint32_t led_timer = HAL_GetTick();

    led_blink(4);

    uint16_t blink_rate = BLINK_FAST;
    uint8_t state = SYSTEM_GPSACQ;

    // DEBUG EMZ FIXME
//    state = SYSTEM_IDLE;

    uint32_t gps_polltimer = 0;
    uint64_t fix_acq_starttime = 0;
    uint64_t nextwspr_time = 0;
    uint8_t nextwspr_time_valid = 0;
    uint64_t last_wspr_tx_time = 0;
    uint64_t idle_blink_last = 0;

    uint8_t packet_type = 0;

    // Transmit pilot tone to test TX on bootup
    HAL_Delay(1000);
    wspr_pilot_tone();
    adc_stop();
    HAL_Delay(1000);

//    __DBGMCU_CLK_ENABLE() ; // (RCC->APB2ENR |= (RCC_APB2ENR_DBGMCUEN))
//    HAL_EnableDBGStopMode();  //  SET_BIT(DBGMCU->CR, DBGMCU_CR_DBG_STOP);

////////////////////////////////////////////
///// TODO: MUST synchro RTC to start of 1PPS second to make sure we actually start wspr on time with 1s granularity
/////////////////////////////////////////
    while (1)
    {
    	// Every 10 minutes, wake up and try to wspr
    	if(state == SYSTEM_IDLE && (rtc_timestamp_seconds() - last_wspr_tx_time >= 60 * 10))
    	{
    		state = SYSTEM_GPSACQ;
    	}

        // Update fix status every 2 seconds, only if the GPS is powered on
        if(rtc_timestamp_seconds() - gps_polltimer >= 3)
        {
            gps_polltimer = rtc_timestamp_seconds();

            if(gps_ison())
            {
            	HAL_GPIO_WritePin(LED_BLUE, 1);
            	HAL_Delay(50);
            	HAL_GPIO_WritePin(LED_BLUE, 0);


            	gps_update_data();

            	// If odd minute
            	if(gps_getdata()->minute % 2)
            	{
            		// Wait until even minute plus one second, coming soon
            		nextwspr_time = rtc_timestamp_seconds() + (60 - (gps_getdata()->second * 1));
                    nextwspr_time_valid = 1;

            	}
            	// If even minute
            	else
            	{
            		// Wait until odd minute, one minute and some change away
            		nextwspr_time = rtc_timestamp_seconds() + 60 + (60 - (gps_getdata()->second * 1));
                    nextwspr_time_valid = 1;
            	}
            }
        }



        switch(state)
        {
            // Idling: sleep and wait for RTC timeslot trigger
            case SYSTEM_IDLE:
            {
            	// Don't blink with the usual method
                blink_rate = BLINK_DISABLE;

                // If we haven't blinked for a while, blink now
                if(rtc_timestamp() - idle_blink_last > 10 * 1000)
                {
                	HAL_GPIO_WritePin(LED_BLUE, 1);
                	HAL_Delay(20);
                	HAL_GPIO_WritePin(LED_BLUE, 0);
                	idle_blink_last = rtc_timestamp();
                }

                // Go into stop mode for ~1s or so
                __sleep_enter_stop();

                // TODO: Eventually use GPS time to calibrate the RTC maybe/trim RTC clock

            } break;


            // Attempt to acquire GPS fix
            case SYSTEM_GPSACQ:
            {
                //blink_rate = BLINK_FAST;
                blink_rate = BLINK_DISABLE;

                if(!gps_ison())
                {
                	fix_acq_starttime = rtc_timestamp_seconds();
                    gps_poweron(); // power on and initialize GPS module
                }

                // TODO: Move GPS processing into here from above!


                // If 3d fix with a decent enough precision
                if( ((gps_getdata()->fixtype == 2) || (gps_getdata()->fixtype == 3)) && gps_getdata()->pdop < 10 && nextwspr_time_valid == 1)
                {
                    // Disable GPS module
                    gps_poweroff();

                    // TODO: Set RTC from GPS time

                    // TODO: Set RTC for countdown to next transmission timeslot!

                    // TODO: Set wspr countdown timer for this transmission!
                    fix_acq_starttime = 0;
                    state = SYSTEM_WSPRTX;
                    adc_start();
                }
                // If no decent fix in 3 minutes
                else if(rtc_timestamp_seconds() - fix_acq_starttime >= 60 * 3)
                {
                	// Flash error code and go to idle, try again next time
                	led_blink(4);
                    gps_poweroff();
                    fix_acq_starttime = 0;
                    last_wspr_tx_time = rtc_timestamp_seconds(); // repeat acq/tx cycle after big time delay
                	state = SYSTEM_IDLE;
                }
                else
                {
                	// We're waiting for a GPS fix, might as well sleep and let the GPS get a fix.
            		// Enter stop mode for 1 second. Blink in the GPS code above.
                    __sleep_enter_stop();
                }

            } break;


            // Wait for wspr timeslot and start transmitting
            case SYSTEM_WSPRTX:
            {
            	//blink_rate = BLINK_MED;
                blink_rate = BLINK_DISABLE;


            	// If we're after the minute but not more than 2s after the minute, start tx
            	if(rtc_timestamp_seconds() >= nextwspr_time)
            	{
            		if(rtc_timestamp_seconds() < nextwspr_time + 2)
            		{
            			volatile double latitude_flt = (double)gps_getdata()->latitude / 10000000.0;
            			volatile double longitude_flt = (double)gps_getdata()->longitude / 10000000.0;
            			volatile uint8_t grid_locator[7];

            			__calc_gridloc(grid_locator, latitude_flt, longitude_flt);

                        // TODO: Switch between alternate and standard packet
						wspr_transmit(grid_locator, packet_type);
                        packet_type = !packet_type; // alternate packet type
						last_wspr_tx_time = rtc_timestamp_seconds();
						state = SYSTEM_IDLE;
                        adc_stop();
            		}
            		else
            		{
            			// Window was missed, go back to idle, and try again after time delay
						last_wspr_tx_time = rtc_timestamp_seconds();
            			state = SYSTEM_IDLE;
                        adc_stop();
            		}
                    nextwspr_time_valid = 0; // invalidate wspr time
                }
            	else
            	{
                	HAL_GPIO_WritePin(LED_BLUE, 1);
                	HAL_Delay(50);
                	HAL_GPIO_WritePin(LED_BLUE, 0);
                	HAL_Delay(50);
                	HAL_GPIO_WritePin(LED_BLUE, 1);
                	HAL_Delay(50);
                	HAL_GPIO_WritePin(LED_BLUE, 0);

            		// Enter stop mode for 1 second
                    __sleep_enter_stop();
            	}

            } break;

        }

		#ifndef LED_DISABLE
			if((blink_rate != BLINK_DISABLE) && (HAL_GetTick() - led_timer > blink_rate))
			{
				__ledpulse();
				led_timer = HAL_GetTick();
			}

			if((blink_rate != BLINK_DISABLE) && (statled_ontime && HAL_GetTick() - statled_ontime > 10))
			{
				HAL_GPIO_WritePin(LED_BLUE, 0);
				statled_ontime = 0;
			}
		#endif

    }
}


static void __sleep_enter_stop(void)
{
	// Save ms unix timestamp before we enter sleep mode
	HAL_SuspendTick();
	uint64_t start = rtc_timestamp();

	__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
	HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);

	// Calculate how long we were asleep
	uint32_t timedelta = rtc_timestamp() - start;

	// Increment systick by this value to keep all timing happy
	HAL_IncTickBy(timedelta);
	HAL_ResumeTick();

	// We have woken up! Clear wakeup flag
	__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
}

static void __ledpulse(void)
{
    HAL_GPIO_WritePin(LED_BLUE, 1);
	statled_ontime = HAL_GetTick();
}

static void __calc_gridloc(char *dst, double lat, double lon)
{
	int o1, o2, o3;
	int a1, a2, a3;
	double remainder;
	// longitude
	remainder = lon + 180.0;
	o1 = (int)(remainder / 20.0);
	remainder = remainder - (double)o1 * 20.0;
	o2 = (int)(remainder / 2.0);
	remainder = remainder - 2.0 * (double)o2;
	o3 = (int)(12.0 * remainder);

	// latitude
	remainder = lat + 90.0;
	a1 = (int)(remainder / 10.0);
	remainder = remainder - (double)a1 * 10.0;
	a2 = (int)(remainder);
	remainder = remainder - (double)a2;
	a3 = (int)(24.0 * remainder);
	dst[0] = (char)o1 + 'A';
	dst[1] = (char)a1 + 'A';
	dst[2] = (char)o2 + '0';
	dst[3] = (char)a2 + '0';
	dst[4] = (char)o3 + 'A';
	dst[5] = (char)a3 + 'A';
	dst[6] = (char)0;
}