C Port of RADE V2#14
Conversation
- rade_v2_core.h: shared typedefs and function declarations
- rade_v2_constants.h: generated constants (latent_dim=56, fps=4, features=21)
- rade_enc_v2.{h,c} + data: CoreEncoderStatefull inference (DenseNet, 5x GRU+dilated conv)
- rade_dec_v2.{h,c} + data: CoreDecoderStatefull inference (DenseNet, 5x GRU+GLU+conv)
- rade_sync.{h,c} + data: FrameSyncNet inference (3-layer feedforward)
- rade_enc_v2_test.c, rade_dec_v2_test.c: stdin/stdout test tools
- opus-nnet.c.diff: raise MAX_CONV_INPUTS_ALL to 2048 for V2 decoder conv5 (896 ch * k=2)
- BuildOpus.cmake: apply opus-nnet.c.diff patch during build
- CMakeLists.txt: add RADE_V2_ML_SOURCES to librade, add test executables
Encoder and decoder validated against Python reference (n() disabled):
max diff ~0.014 (float32 arithmetic + int8 quantisation noise).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Model 250725 trained with bottleneck=0 (no tanh on z_dense output). ACTIVATION_TANH was producing z≈±0.76 instead of natural range, causing decoder loss ~7.5. Linear activation gives loss 0.096. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
C port of tx2.py/RADEv2Transmitter. V2 frame structure is simpler than V1: Ns=2 pure data symbols per frame (no pilots), Nc=14, M=128, Ncp=32. EOO is 6 x pend_cp symbols scaled by pilot_gain_eoo_v2. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
C port of rx2.py / RADEv2Receiver: - rade_rx_v2.h/.c: full receiver state machine — CP autocorrelation, signal/sine detection, IIR timing+freq tracking, symbol extraction, OFDM demod via rade_v2_ofdm_demod_frame, FrameSyncNet even/odd frame sync, EOO detection via channel sparsity metric, stateful decoder via rade_core_decoder_v2 - radae_v2_rx_nopy.c: CLI binary (nin-based read loop, features to stdout) - CMakeLists.txt: adds rade_rx_v2.c to RADE_V2_DSP_SOURCES, adds radae_v2_rx executable target Verified: C tx -> C rx pipeline, loss 0.083 (threshold 0.2), n_acq=1, EOO detected correctly at noiseless SNR. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add rade_v2_ofdm_demod_frame() and rade_v2_ofdm_eoo_metric() needed by the V2 receiver. Also adds DFT matrix (Wfwd), per-carrier phase correction, and pend_td fields to the rade_v2_ofdm struct. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
@peterbmarks @tmiw - thinking about naming conventions for V1 & V2 code in this repo. So Claude has come up with In drowe67/radae#70 I've renamed the ctests |
Adds --write_snr_est <filename> flag to write per-symbol snr_est_dB values to a binary float32 file, matching rx2.py behaviour. Used by ctests radae_nopy_v2_rx_snr_high and radae_nopy_v2_rx_snr_low. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
I think rade_vX_tx etc is good. (Leaves the door open to v3) |
You mean the API? Lets take a look at that next. |
|
Using a version in the function names would be good. Also, what about adding a function to the existing API to indicate which version to use (and then eventually default to using the RADEV2 implementations or something)? For example: struct rade* rade = rade_open(...); // uses RADEV1, will eventually be the same as rade_open_with_version(RADE_VERSION_2, ...)
struct rade* rade2 = rade_open_with_version(RADE_VERSION_2, ...);Then internally (for example): struct rade* rade_open_with_version(int version, ...) {
struct rade* result = malloc(sizeof(struct rade));
if (version == RADE_VERSION_1) result->impl = rade_v1_get_impl();
else if (version == RADE_VERSION_2) result->impl = rade_v2_get_impl();
...
// additional initialization here
return result;
}
int rade_tx(struct rade* rade, ...) {
return rade->impl.tx(rade, ...);
}Anyway, just thinking from the perspective of RADEV2 being a thing for potentially quite a while before there's a V3. If we want something quick we can just have e.g. EDIT: whoops, I think @drowe67 and I commented at the same time. We can look at rade_api after for sure. |
That's fine - what you are brainstorming is the topic I was suggesting we discuss. TBH I'm not as worried about breaking the API at this stage - it's pretty early days and the fix (like adding a version argument to But I'm not mandating anything atm - lets ponder it and form a consensus. |
|
I'm not sure that sharing rade_open returning a pointer to the rade structure can work as that struct might change with future versions. It contains rade_tx_state and rade_rx_state and they could also be different. Maybe we should just go with rade and rade2? Also, it's good to build a few clients apps that use the API and collect ideas of what the API should expose. |
There could also be a private state inside the struct that could be initialized/used in a version dependent manner. But as mentioned, if we're already going to be breaking compatibility anyway... |
Claude doing his magic, backed by ctests from drowe67/radae#70.
See https://github.com/drowe67/radae_nopy/tree/dr-radev2 for V2 C Port test results.
WIP - do not merge.
The on-air waveform is not frozen and is likely to change at anytime