diff --git a/G4:G431/CORDIC/Readme.md b/G4:G431/CORDIC/Readme.md new file mode 100644 index 0000000..8ff2208 --- /dev/null +++ b/G4:G431/CORDIC/Readme.md @@ -0,0 +1,241 @@ +# STM32 Astronomy & CORDIC Test Project + +## Introduction + +This project is designed for **STM32G4** microcontrollers (specifically the STM32G431) and provides a +framework for: + +- **Astronomical coordinate transformations** (Alt-Az vs HA-Dec, RA/HA, Local Sidereal Time). +- **Atmospheric refraction** modelling using a single-precision adaptation of SOFA/ERFA algorithms. +- **Performance comparison** between standard `math.h` functions and the hardware-accelerated **CORDIC** unit + built into the STM32G4. +- **Interactive control** via a serial (UART) command interface. +- **Precise timing** measurements using a 32-bit timer. + +All calculations are performed in **single-precision floating-point** for speed on microcontrollers without +hardware double-precision support. + +--- + +## Hardware Platform + +- **MCU**: STM32G431 (or any STM32G4 with CORDIC). +- **Clock**: HSE oscillator, system frequency defined by `SysFreq` (usually 170 MHz). +- **GPIO**: + - **PC6** - LED (active high). + - **PC13** - User button (pull-down, pressed = high). +- **USART1**: + - **PA9** - TX (alternate function 7). + - **PA10** - RX (alternate function 7). +- **Timers**: + - **SysTick** - 1-ms system tick (`Tms`). + - **TIM2** - 32-bit counter running at 1-MHz for microsecond measurements. + +--- + +## Building and Flashing + +The project is intended for use with **GCC ARM** toolchain. Key build definitions: + +- `SysFreq` - system clock frequency in Hz (e.g., 170000000). +- `BUILD_NUMBER` and `BUILD_DATE` are defined in `version.inc` (generated by `make`). + +Flashing can be done via SWD using st-link (`make flash`), via DFU (`make dfuboot`) or via USART1 bootloader +(`make boot`). + +--- + +## Software Architecture + +The system runs an infinite loop in `main()` that: +- Services the UART (receiving and sending data). +- Parses incoming commands via `parse_cmd()`. +- Blinks the LED every 500 ms. +- Detects button presses and measures press/release duration using TIM2. + +--- + +## Module Descriptions + +### 1. `hardware.c/h` - Low-level initialisation + +- `gpio_setup()`: configures GPIOs, enables SysTick (1 ms interrupt), and initialises TIM2 for microsecond counting. +- Provides macros for LED and key handling: + - `LED_ON()`, `LED_OFF()`, `LED_TOGG()` + - `KEY_PRESSED()` - returns true if PC13 is high. +- `timer_start()`, `timer_stop()`, `timer_read()` - control TIM2 for precise time measurements. + +### 2. `usart.c/h` - UART1 with DMA and ring buffers + +- Uses **circular DMA** for RX and **normal DMA** for TX. +- RX buffer size: 256 bytes (DMA) + 512 bytes (ring buffer). +- TX buffer size: 512 bytes (ring buffer) + 256 bytes (DMA). +- Interrupts: + - USART1 interrupt - detects end-of-line (`\n`) and triggers line processing. + - DMA1 Channel 1 (RX) - handles receive error (buffer overflow). + - DMA1 Channel 2 (TX) - signals transmit completion. +- Functions: + - `usart_setup(uint32_t baud)` - initialises UART at the given baud rate. + - `usart_sendstr(const char*)` - queues a string for transmission. + - `usart_getline()` - returns a pointer to a null-terminated buffer containing one line (ends with `\n`), or `NULL` if no complete line. + - `usart_process()` - must be called frequently to feed the TX buffer and move RX data from DMA to the ring buffer; returns status flags. + +### 3. `commproto.cpp` - Command protocol + +- Parses commands of the form: + ``` + command [args] [= value] + ``` +- Built-in commands and variables (see **Usage** below). +- Uses a **hash table** (compile-time hashing) to dispatch commands efficiently. +- Supports **getter** and **setter** for floating-point variables (az, dec, ha, ra, zd, humid, press, temp). +- Command results are sent back via the UART send function. +- Error codes are defined in `commproto.h`. + +### 4. `astro.c/h` - Astronomical calculations + +All angles are in **degrees** unless otherwise noted. + +- **Time**: + - `MJD_from_unix(uint32_t t)` - converts Unix time (seconds since 1970-01-01) to Modified Julian Date (MJD). + - `LST_from_unix(uint32_t t)` - computes Local Sidereal Time (in hours) for the observer's longitude (`long_hrs`). +- **Coordinate transformations**: + - `altaz_to_hadec()` - convert Alt-Az to HA-Dec. + - `hadec_to_altaz()` - convert HA-Dec to Alt-Az. + - `ha_to_ra()` and `ra_to_ha()` - convert between Hour Angle and Right Ascension given LST. +- **Refraction** (single-precision adaptation of ERFA `eraRefco`): + - `refco_f32(float P, float T, float RH, float wavelength, float *refa, float *refb)` - computes A and B coefficients for the refraction model: + `dz = A*tan(z) + B*tan(z)^3` (z = zenith distance). + - `refraction(float P, float T, float RH, float zd)` - returns the refraction correction in degrees. +- The observer's **latitude** and **longitude** are fixed in the code (`lat_rad` and `long_hrs`). They may be changed by editing `astro.c`. + +### 5. `cordic.c/h` - Hardware CORDIC wrappers + +- Interfaces with the STM32G4 CORDIC coprocessor. +- Functions: + - `cordic_sincos(float angle, float *sin, float *cos)` - computes sine and cosine in one call. + - `cordic_sin(float angle)`, `cordic_cos(float angle)` - individual functions. + - `cordic_atan(float x)` - returns atan(x), x in [-1,1]. + - `cordic_sqrt(float x)` - returns sqrt(x) for x > 0. + - `cordic_log(float x)` - returns natural logarithm of x (x > 0). +- **Precision**: Q1.31 fixed-point (32-bit) with 5 iterations (`CORDIC_CSR_PRECISION = 5`). +- Input scaling and normalisation are handled inside each function. + +### 6. `strfunc.c/h` - String utilities + +- Number formatting: + - `u2str()`, `i2str()`, `uhex2str()` - convert integers to strings. + - `float2str(float x, uint8_t prec)` - convert float to a compact string with optional scientific notation. +- Number parsing: + - `getnum()`, `getint()`, `getfloat()` - parse numbers (decimal, hex, binary, signed, floating with exponent). +- `omit_spaces()` - skip leading whitespace. + +### 7. `ringbuffer.c/h` - Circular buffer + +- Generic ring buffer for arbitrary data. +- Functions: + - `RB_write()`, `RB_read()`, `RB_readto()` (read until a delimiter), `RB_hasbyte()`, `RB_datalen()`, `RB_clearbuf()`. + +### 8. `test.c/h` - Performance tests + +- Runs a fixed number of iterations (`N_TESTS = 1000`) on random data. +- Measures execution time using TIM2 (microseconds). +- Tests available: + - `math` functions: `sinf`, `cosf`, `atanf`, `sqrtf`, `logf`. + - `CORDIC` functions: `sincos`, `sin`, `cos`, `atan`, `sqrt`, `log`. + - **Astronomy** tests: + - Coordinate transformations (Alt-Az vs HA-Dec) - measures time per conversion. + - Refraction cycle (ha-dec to alt-az, apply refraction and convert back) - includes `refraction()`. + - LST calculation - measures time per LST computation. + +--- + +## Usage: UART Commands + +Connect to the USART1 (default baud rate **115200**, 8N1) using a terminal program. The device echoes +responses to commands. + +General syntax: +``` +command [args] [= value] +``` +- Without `= value`, the command acts as a **getter** (prints current value) or command that don't needs + argument. +- With `= value`, it acts as a **setter**. + +All floating-point values are printed with 7 decimal places. + +### Available Commands + +| Command | Description | +|---------|-------------| +| `help` | Show this help message. | +| `testtimer` | Test the microsecond timer for 1 second (prints elapsed us). | +| `testc = ` | Test CORDIC function: `sincos`, `sin`, `cos`, `atan`, `sqrt`, `log`. Prints time in us. | +| `testm = ` | Test `math.h` function: `sin`, `cos`, `atan`, `sqrt`, `log`. Prints time in us. | +| `sincos = ` | Compute sin/cos for given angle (degrees) using both `math.h` and CORDIC, print results. | +| `testastro = ` | Test astronomy functions: `coords` (transform cycle), `lst` (LST calculation), `refr` (refraction cycle). Prints time in us and ms. | +| `sets [= 0/1]` | Choose sine/cos implementation: `0` = `math.h`, `1` = CORDIC. | +| `time [= unix_sec]` | Show current Unix time, MJD, and LST. If `= unix_sec` is given, sets the system time. | +| `az [= value]` | Azimuth (degrees, -180..180). | +| `dec [= value]` | Declination (degrees, -90..90). | +| `ha [= value]` | Hour Angle (degrees, -180..180). | +| `ra [= value]` | Right Ascension (degrees, 0..360). | +| `zd [= value]` | Zenith Distance (degrees, 0..90). | +| `humid [= value]` | Relative humidity (0..1). | +| `press [= value]` | Atmospheric pressure (hPa, 0..1200). | +| `temp [= value]` | Temperature (degC, -273.15..100). | +| `hara = 0/1` | Convert HA to RA (`0`) or RA to HA (`1`) using current LST. Updates the corresponding variable. | +| `azhd = 0/1` | Convert Alt-Az to HA-Dec (`0`) or HA-Dec to Alt-Az (`1`). Updates relevant variables (az, zd or ha, dec, ra). | +| `refr` | Compute refraction (arcseconds) for current `zd`, pressure, temperature, humidity. Also prints A/B coefficients. | + +**Note**: The `azhd` and `hara` commands automatically use the current system time (from the last `time` setter) to compute LST. + +--- + +## Testing and Performance + +The test commands (`testc`, `testm`, `testastro`) measure execution time over 1000 iterations on random input +data. This allows you to compare the speed of hardware CORDIC against software `math.h` and evaluate the +overall efficiency of the astronomical pipeline. + +Example outputs (typical for STM32G4 @ 170 MHz): + +``` +testc = sincos +TIMEus=425 +``` + +``` +testm = sin +TIMEus=884 +``` + +``` +testastro = coords +TIMEus=159680 +TIMEms=159 +``` + +These numbers help in optimising real-time applications such as telescope control. + +--- + +## Notes and Customisation + +- **Observer coordinates**: The latitude (43.6535278deg) and longitude (41.44143375deg) are hard-coded in + `astro.c` as `lat_rad` and `long_hrs`. Modify these for your location. +- **CORDIC precision**: The number of iterations is set to 5 (`CORDIC_CSR_DEF`). You can increase it for + better accuracy at the cost of speed. +- **Refraction model**: The model works best for zenith distances up to ~80deg. It uses a wavelength of + 0.55um (optical) by default. +- **Baud rate**: Default is 115200. Change the argument to `usart_setup()` in `main()` if needed. +- **DMA buffers**: Sizes are defined in `usart.h`. Adjust if you experience overruns. + +--- + +## License + +This project is released under the **GNU General Public License v3**. See the header of each source file for +details. + diff --git a/G4:G431/CORDIC/cordic.bin b/G4:G431/CORDIC/cordic.bin index 6dfcaff..695f242 100755 Binary files a/G4:G431/CORDIC/cordic.bin and b/G4:G431/CORDIC/cordic.bin differ diff --git a/G4:G431/CORDIC/cordic.files b/G4:G431/CORDIC/cordic.files index 3f705b7..e344396 100644 --- a/G4:G431/CORDIC/cordic.files +++ b/G4:G431/CORDIC/cordic.files @@ -1,3 +1,4 @@ +Readme.md astro.c astro.h commproto.cpp diff --git a/G4:G431/CORDIC/hardware.c b/G4:G431/CORDIC/hardware.c index 3d3edd1..f8b7413 100644 --- a/G4:G431/CORDIC/hardware.c +++ b/G4:G431/CORDIC/hardware.c @@ -28,24 +28,12 @@ void sys_tick_handler(){ } static void timer_setup(){ - RCC->APB1ENR1 |= RCC_APB1ENR1_TIM3EN; + RCC->APB1ENR1 |= RCC_APB1ENR1_TIM2EN; __DSB(); - TIM3->CR1 = 0; // disable counter - TIM3->PSC = 84; // 85 MHz / 85 = 1 MHz - TIM3->ARR = 0xFFFFFFFF; // 32-bit auto-reload (maximum) -} - -void timer_start(){ - TIM3->CNT = 0; - TIM3->CR1 = TIM_CR1_CEN; -} - -void timer_stop(){ - TIM3->CR1 = 0; -} - -uint32_t timer_read(){ - return TIM3->CNT; + TIM2->CR1 = 0; // disable counter + TIM2->PSC = 84; // 85 MHz / 85 = 1 MHz + TIM2->ARR = 0xFFFFFFFF; // 32-bit auto-reload (maximum) + TIM2->EGR = TIM_EGR_UG; } void gpio_setup(){ @@ -62,7 +50,7 @@ void gpio_setup(){ // count milliseconds SysTick_Config(SysFreq / 1000); - // Setup TIM3 for microsecond counting + // Setup 32-bit TIM2 for microsecond counting timer_setup(); } diff --git a/G4:G431/CORDIC/hardware.h b/G4:G431/CORDIC/hardware.h index ce5c28c..83670f8 100644 --- a/G4:G431/CORDIC/hardware.h +++ b/G4:G431/CORDIC/hardware.h @@ -18,6 +18,8 @@ #pragma once +#include + // KEY (intpullup->0) - PC13 // LED - PC6 #define KEY_PORT GPIOC @@ -38,6 +40,15 @@ extern volatile uint32_t Tms; void gpio_setup(); -void timer_start(); -void timer_stop(); -uint32_t timer_read(); +TRUE_INLINE void timer_start(){ + TIM2->CNT = 0; + TIM2->CR1 = TIM_CR1_CEN; +} + +TRUE_INLINE void timer_stop(){ + TIM2->CR1 = 0; +} + +TRUE_INLINE uint32_t timer_read(){ + return TIM2->CNT; +} diff --git a/G4:G431/CORDIC/test.c b/G4:G431/CORDIC/test.c index 12472f0..f22bb98 100644 --- a/G4:G431/CORDIC/test.c +++ b/G4:G431/CORDIC/test.c @@ -20,6 +20,7 @@ #include #include +#include "astro.h" #include "test.h" #include "hardware.h" #include "cordic.h" @@ -90,38 +91,95 @@ static uint32_t run_test2(void (*gen)(), void (*func)(float, float*, float*)){ } // ------------- math.h tests ------------- -uint32_t test_math_sin(){ +uint32_t test_math_sin(){ // 0.9us per cycle return run_test(fill_random_sin_cos, sinf); } -uint32_t test_math_cos(){ +uint32_t test_math_cos(){ // 0.9us per cycle return run_test(fill_random_sin_cos, cosf); } -uint32_t test_math_atan(){ +uint32_t test_math_atan(){ // 0.9us per cycle return run_test(fill_random_atan, atanf); } -uint32_t test_math_sqrt(){ +uint32_t test_math_sqrt(){ // 0.16us per cycle return run_test(fill_random_sqrt, sqrtf); } -uint32_t test_math_log(){ +uint32_t test_math_log(){ // 0.9us per cycle return run_test(fill_random_log, logf); } // ------------- CORDIC tests ------------- -uint32_t test_cordic_sincos(){ +uint32_t test_cordic_sincos(){ // 0.4us per cycle return run_test2(fill_random_sin_cos, cordic_sincos); } -uint32_t test_cordic_sin(){ +uint32_t test_cordic_sin(){ // 0.4us per cycle return run_test(fill_random_sin_cos, cordic_sin); } -uint32_t test_cordic_cos(){ +uint32_t test_cordic_cos(){ // 0.4us per cycle return run_test(fill_random_sin_cos, cordic_cos); } -uint32_t test_cordic_atan(){ +uint32_t test_cordic_atan(){ // 0.12us per cycle return run_test(fill_random_atan, cordic_atan); } -uint32_t test_cordic_sqrt(){ +uint32_t test_cordic_sqrt(){ // 0.4us per cycle return run_test(fill_random_sqrt, cordic_sqrt); } -uint32_t test_cordic_log(){ +uint32_t test_cordic_log(){ // 0.4us per cycle return run_test(fill_random_log, cordic_log); } + +// ------------- Astronomy tests ------------- +// test coordinates transformation: hor2eq and eq2hor +uint32_t test_astro_coordsTransform(){ + volatile float az = 11.3f, alt = 89.3f, ha, dec; + timer_start(); + for(int i = 0; i < N_TESTS; ++i){ + // 160us per cycle for math.h sin/cos + // 15us per cycle for CORDIC sin/cos + altaz_to_hadec(alt, az, (float*)&ha, (float*)&dec); + hadec_to_altaz(ha, dec, (float*)&alt, (float*)&az); + if((az += 9.51f) > 359.99f) az -= 359.9f; + if((alt -= 1.76f) < 9.9f) alt += 79.f; + (void) ha; + (void) dec; + } + timer_stop(); + return timer_read(); +} + +// test refraction correction for HA-DEC (eq2hor->refr->hor2eq) +uint32_t test_astro_refraction(){ + float az = 11.3f, alt = 89.3f, ha, dec; + float phpa = 800.f, tc = 10.f, rh = 0.7f; + volatile float newha, newdec, newalt; + timer_start(); + for(int i = 0; i < N_TESTS; ++i){ + // 260us per cycle for math.h sin/cos + // 34us per cycle for CORDIC sin/cos + altaz_to_hadec(alt, az, &ha, &dec); + hadec_to_altaz(ha, dec, &alt, &az); + newalt = alt + refraction(phpa, tc, rh, 90.f-alt); + altaz_to_hadec(newalt, az, (float*)&newha, (float*)&newdec); + (void) newha; + (void) newdec; + if((az += 9.51f) > 359.99f) az -= 359.9f; + if((alt -= 1.76f) < 9.9f) alt += 79.f; + } + timer_stop(); + return timer_read(); +} + +// test LST calculation +uint32_t test_astro_LST(){ + uint32_t *uarr = (uint32_t*) arr; + volatile float result; + for(int i = 0; i < N_TESTS; ++i){ // fill random data in 21th century + uarr[i] = next_rand() % 3155673599 + 978307200; + } + timer_start(); + for(int i = 0; i < N_TESTS; ++i){ // 1.5us per cycle + result = LST_from_unix(uarr[i]); + (void) result; + } + timer_stop(); + return timer_read(); +} diff --git a/G4:G431/CORDIC/test.h b/G4:G431/CORDIC/test.h index b84248a..158d2c3 100644 --- a/G4:G431/CORDIC/test.h +++ b/G4:G431/CORDIC/test.h @@ -34,3 +34,8 @@ uint32_t test_cordic_cos(); uint32_t test_cordic_atan(); uint32_t test_cordic_sqrt(); uint32_t test_cordic_log(); + +// Astronomy tests +uint32_t test_astro_coordsTransform(); +uint32_t test_astro_refraction(); +uint32_t test_astro_LST(); diff --git a/G4:G431/CORDIC/version.inc b/G4:G431/CORDIC/version.inc index 6acf276..2d4dc11 100644 --- a/G4:G431/CORDIC/version.inc +++ b/G4:G431/CORDIC/version.inc @@ -1,2 +1,2 @@ -#define BUILD_NUMBER "57" -#define BUILD_DATE "2026-09-05" +#define BUILD_NUMBER "75" +#define BUILD_DATE "2026-09-08"