11/**
2- * @file rtcpsummary.c RTCP summary module
3- * Output RTCP stats at the end of a call if there are any
2+ * @file rgrtcpsummary.c RG Nets RTCP summary module
3+ * Output RTCP stats and MOS score at the end of a call if there are any
44 *
5- * Copyright (C) 2010 - 2018 Alfred E. Heggestad
5+ * Copyright (C) 2025 Michael D. Ketchel
66 */
77#include <re.h>
88#include <baresip.h>
99
1010
11+ // Clamp value between min and max
12+ double clamp (double value , double min , double max ) {
13+ if (value < min ) return min ;
14+ if (value > max ) return max ;
15+ return value ;
16+ }
17+
18+ // Calculate MOS using ITU-T E-model approximation
19+ double calculate_mos (double rtt_ms , double tx_jitter_ms , double rx_jitter_ms , double packet_loss_percent ) {
20+ // Step 1: Calculate mouth-to-ear delay (ms)
21+ double delay = rtt_ms + tx_jitter_ms + rx_jitter_ms ;
22+
23+ // Step 2: Calculate delay impairment
24+ double delay_impairment = 0.024 * delay ;
25+ if (delay > 177.3 ) {
26+ delay_impairment += 0.11 * (delay - 177.3 );
27+ }
28+
29+ // Step 3: Calculate loss impairment (simplified)
30+ double loss_impairment = 2.5 * packet_loss_percent ;
31+
32+ // Step 4: Calculate R-factor
33+ double R = 94.2 - delay_impairment - loss_impairment ;
34+ R = clamp (R , 0.0 , 100.0 );
35+
36+ // Step 5: Calculate MOS from R-factor
37+ double MOS = 1.0 + 0.035 * R + (R * (R - 60.0 ) * (100.0 - R ) * 7.0e-6 );
38+ MOS = clamp (MOS , 1.0 , 5.0 );
39+
40+ return MOS ;
41+ }
42+
43+
1144static void print_rtcp_summary_line (const struct call * call ,
1245 const struct stream * s )
1346{
1447 const struct rtcp_stats * rtcp ;
1548 rtcp = stream_rtcp_stats (s );
1649
1750 if (rtcp && (rtcp -> tx .sent || rtcp -> rx .sent )) {
51+
52+
53+
1854 info ("\n" );
1955 /*
2056 * Add a stats line to make it easier to parse result
@@ -32,6 +68,7 @@ static void print_rtcp_summary_line(const struct call *call,
3268 "JI=%.1f,%.1f;" /* Jitter RX, TX in ms */
3369 "DL=%.1f;" /* RTT in ms */
3470 "IP=%J,%J;" /* Local, Remote IPs */
71+ "MOS=%.2f;" /* MOS score */
3572 "\n"
3673 ,
3774 call_setup_duration (call ) * 1000 ,
@@ -46,7 +83,15 @@ static void print_rtcp_summary_line(const struct call *call,
4683 1.0 * rtcp -> tx .jit /1000 ,
4784 1.0 * rtcp -> rtt /1000 ,
4885 sdp_media_laddr (stream_sdpmedia (s )),
49- sdp_media_raddr (stream_sdpmedia (s )));
86+ sdp_media_raddr (stream_sdpmedia (s )),
87+ calculate_mos (
88+ 1.0 * rtcp -> rtt /1000 ,
89+ 1.0 * rtcp -> tx .jit /1000 ,
90+ 1.0 * rtcp -> rx .jit /1000 ,
91+ // A naive handling of packet loss here. There is likely a better way.
92+ (1.0 * (rtcp -> rx .lost + rtcp -> tx .lost ) / (rtcp -> rx .sent + rtcp -> tx .sent ))
93+ )
94+ );
5095 }
5196 else {
5297 /*
0 commit comments