@@ -1640,22 +1640,37 @@ static canard_filter_t rx_filter_fuse(const canard_filter_t a, const canard_filt
16401640// Filter selectivity metric; see the Cyphal/CAN specification. Greater values ==> stronger filter.
16411641static byte_t rx_filter_rank (const canard_filter_t a ) { return popcount (a .extended_mask ); }
16421642
1643- // Modifies the filter array such that the new filter is accepted. See Cyphal/CAN Spec.
1643+ // Modifies the filter array such that the new filter is accepted. See Cyphal/CAN Spec. Requires count>0.
16441644static void rx_filter_coalesce_into (const size_t count , canard_filter_t * const into , const canard_filter_t new )
16451645{
1646- // Currently we're using a simplified fast algorithm that doesn't attempt fusing existing entries with each other.
1647- // This may result in suboptimal configurations but is faster.
1648- size_t index = 0 ;
1649- byte_t best_rank = 0 ;
1646+ CANARD_ASSERT ((count > 0U ) && (into != NULL ));
1647+ // Find the most similar pair by trying all pair combinations (including the incoming filter).
1648+ // This is O(N^2) but yields better final filter quality than fusing only with the incoming filter.
1649+ // The complexity is acceptable because N is controlled by the application and is small;
1650+ // plus, this behavior can be disabled completely by using at least as many filters as there are subscriptions.
1651+ bool initialized = false;
1652+ size_t best_i = 0 ;
1653+ size_t best_j = count ;
1654+ byte_t best_rank = 0 ;
1655+ canard_filter_t best_fuse = { 0 };
16501656 for (size_t i = 0 ; i < count ; i ++ ) {
1651- const byte_t fused_rank = rx_filter_rank (rx_filter_fuse (into [i ], new ));
1652- if (fused_rank >= best_rank ) { // use >= to prefer later filters where v0 is
1653- best_rank = fused_rank ;
1654- index = i ;
1657+ for (size_t j = i + 1U ; j <= count ; j ++ ) { // j==count denotes the incoming filter
1658+ const canard_filter_t f = rx_filter_fuse (into [i ], (j < count ) ? into [j ] : new );
1659+ const byte_t r = rx_filter_rank (f );
1660+ if ((!initialized ) || (r >= best_rank )) { // use >= to prefer later filters where v0 is
1661+ initialized = true;
1662+ best_i = i ;
1663+ best_j = j ;
1664+ best_rank = r ;
1665+ best_fuse = f ;
1666+ }
16551667 }
16561668 }
1657- CANARD_ASSERT (index < count );
1658- into [index ] = rx_filter_fuse (into [index ], new );
1669+ CANARD_ASSERT (initialized && (best_i < count ) && (best_j <= count ));
1670+ into [best_i ] = best_fuse ;
1671+ if (best_j < count ) {
1672+ into [best_j ] = new ;
1673+ }
16591674}
16601675
16611676// Recompute the filter configuration and apply. Returns true on success, false on driver error.
0 commit comments