mirror of
https://github.com/eddyem/stm32samples.git
synced 2026-09-29 21:30:40 +03:00
add astro-tests
This commit is contained in:
241
G4:G431/CORDIC/Readme.md
Normal file
241
G4:G431/CORDIC/Readme.md
Normal file
@@ -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 = <func>` | Test CORDIC function: `sincos`, `sin`, `cos`, `atan`, `sqrt`, `log`. Prints time in us. |
|
||||
| `testm = <func>` | Test `math.h` function: `sin`, `cos`, `atan`, `sqrt`, `log`. Prints time in us. |
|
||||
| `sincos = <angle>` | Compute sin/cos for given angle (degrees) using both `math.h` and CORDIC, print results. |
|
||||
| `testastro = <func>` | 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.
|
||||
|
||||
Binary file not shown.
@@ -1,3 +1,4 @@
|
||||
Readme.md
|
||||
astro.c
|
||||
astro.h
|
||||
commproto.cpp
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stm32g4.h>
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stm32g4.h>
|
||||
|
||||
#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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
#define BUILD_NUMBER "57"
|
||||
#define BUILD_DATE "2026-09-05"
|
||||
#define BUILD_NUMBER "75"
|
||||
#define BUILD_DATE "2026-09-08"
|
||||
|
||||
Reference in New Issue
Block a user