-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathcsma.hh
More file actions
161 lines (150 loc) · 5.6 KB
/
Copy pathcsma.hh
File metadata and controls
161 lines (150 loc) · 5.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#pragma once
#include <algorithm>
#include <cstdint>
#include <random>
struct CsmaConfig {
float threshold_db = -30.0f;
bool sync_only = false;
int poll_ms = 25;
int quiet_ms = 1500;
int cw = 8;
int slot_ms = 500;
bool responder = false;
int responder_quiet_ms = 300;
int responder_dither_ms = 0;
int deaf_limit_ms = 5000;
int busy_limit_ms = 60000;
int idle_credit_ms = 0;
int cold_channel_ms = 10000;
int dcd_detect_ms = 780;
int contenders = -1;
int rank = -1;
int rank_n = 0;
int extra_delay_ms = 0;
};
class CsmaGate {
public:
enum class Verdict { WAIT, TRANSMIT };
enum class Reason { NONE, CLEAR, RESPONDER, BUSY_OVERRIDE, NO_AUDIO };
static constexpr int RANKED_SLOT_MS = 1500;
CsmaGate(const CsmaConfig& cfg, uint32_t seed) : cfg_(cfg), gen_(seed) {
int slot = std::max(1, cfg_.slot_ms);
int window = std::max(2, cfg_.cw) * slot;
if (cfg_.sync_only) {
int det = std::max(1, cfg_.dcd_detect_ms);
if (cfg_.contenders >= 0) {
int slots = std::min(16, std::max(6, 3 * (cfg_.contenders + 1)));
window = std::max(slots * (det + 150), 4 * slot);
} else {
window = std::max(window * 2, 16 * det);
}
if (cfg_.idle_credit_ms >= cfg_.cold_channel_ms)
window = std::max(window / 4, 4 * slot);
} else if (cfg_.idle_credit_ms >= cfg_.cold_channel_ms) {
window = std::max(window / 4, 4 * slot);
}
window_ = window;
if (cfg_.sync_only && !cfg_.responder && cfg_.rank >= 0) {
rank_slot_ = RANKED_SLOT_MS;
window_ = std::max(1, cfg_.rank_n) * rank_slot_;
quiet_needed_ = cfg_.quiet_ms;
contention_ms_ = cfg_.rank * rank_slot_;
} else if (cfg_.responder) {
quiet_needed_ = std::min(cfg_.quiet_ms, cfg_.responder_quiet_ms);
contention_ms_ = cfg_.responder_dither_ms +
slot * std::uniform_int_distribution<int>(0, 3)(gen_) +
std::uniform_int_distribution<int>(0, slot - 1)(gen_);
} else {
int slots = std::max(2, window / slot);
quiet_needed_ = cfg_.quiet_ms;
contention_ms_ = cfg_.extra_delay_ms + slot *
std::uniform_int_distribution<int>(0, slots - 1)(gen_) +
std::uniform_int_distribution<int>(0, slot - 1)(gen_);
}
contention_drawn_ = contention_ms_;
idle_ms_ = std::min(std::max(0, cfg_.idle_credit_ms), quiet_needed_);
}
Verdict step(float level_db, bool capture_alive, bool tx_allowed) {
if (!capture_alive) {
idle_ms_ = 0;
deaf_ms_ += cfg_.poll_ms;
redraw_pending_ = false;
if (deaf_ms_ >= cfg_.deaf_limit_ms) {
reason_ = Reason::NO_AUDIO;
return Verdict::TRANSMIT;
}
return Verdict::WAIT;
}
deaf_ms_ = 0;
if (!tx_allowed || (!cfg_.sync_only && level_db > cfg_.threshold_db)) {
idle_ms_ = 0;
busy_ms_ += cfg_.poll_ms;
redraw_pending_ = true;
if (busy_ms_ >= cfg_.busy_limit_ms) {
reason_ = Reason::BUSY_OVERRIDE;
return Verdict::TRANSMIT;
}
return Verdict::WAIT;
}
if (redraw_pending_ && cfg_.sync_only && !cfg_.responder) {
if (rank_slot_ > 0) {
contention_ms_ = cfg_.rank * rank_slot_;
} else {
episodes_ = std::min(episodes_ + 1, 1);
int slot = std::max(1, cfg_.slot_ms);
int w = cfg_.contenders >= 0
? window_
: (int)std::min<long long>((long long)window_ << episodes_, 60000);
int slots = std::max(2, w / slot);
contention_ms_ = cfg_.extra_delay_ms + slot *
std::uniform_int_distribution<int>(0, slots - 1)(gen_) +
std::uniform_int_distribution<int>(0, slot - 1)(gen_);
}
contention_drawn_ = contention_ms_;
}
redraw_pending_ = false;
busy_ms_ = 0;
idle_ms_ += cfg_.poll_ms;
if (idle_ms_ < quiet_needed_)
return Verdict::WAIT;
if (contention_ms_ > 0) {
contention_ms_ -= cfg_.poll_ms;
return Verdict::WAIT;
}
reason_ = cfg_.responder ? Reason::RESPONDER : Reason::CLEAR;
return Verdict::TRANSMIT;
}
void set_rank(int rank, int n) {
cfg_.rank = rank;
cfg_.rank_n = n;
if (rank < 0) {
rank_slot_ = 0;
return;
}
rank_slot_ = RANKED_SLOT_MS;
window_ = std::max(1, cfg_.rank_n) * rank_slot_;
}
Reason reason() const { return reason_; }
bool quiet_met() const { return idle_ms_ >= quiet_needed_; }
int window_ms() const { return window_; }
int quiet_needed_ms() const { return quiet_needed_; }
int contention_drawn_ms() const { return contention_drawn_; }
int contention_left_ms() const { return std::max(0, contention_ms_); }
int idle_ms() const { return idle_ms_; }
int busy_ms() const { return busy_ms_; }
int deaf_ms() const { return deaf_ms_; }
private:
CsmaConfig cfg_;
std::mt19937 gen_;
int window_ = 0;
int rank_slot_ = 0;
int quiet_needed_ = 0;
int contention_ms_ = 0;
int contention_drawn_ = 0;
int idle_ms_ = 0;
int deaf_ms_ = 0;
int busy_ms_ = 0;
int episodes_ = 0;
bool redraw_pending_ = false;
Reason reason_ = Reason::NONE;
};