2024-12-30 23:45:14 +08:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <cstdint>
|
2024-12-31 16:57:42 +08:00
|
|
|
#include <complex>
|
2025-01-18 15:46:45 +08:00
|
|
|
#include <chrono>
|
|
|
|
#include <thread>
|
2024-12-31 16:57:42 +08:00
|
|
|
|
2024-12-31 17:04:37 +08:00
|
|
|
typedef uint32_t phase_t;
|
|
|
|
#define PHASE_MAX UINT32_MAX
|
|
|
|
|
|
|
|
static phase_t frequency_to_ftw(double f) {
|
|
|
|
return f*(double)PHASE_MAX;
|
2024-12-31 16:57:42 +08:00
|
|
|
}
|
2024-12-30 23:45:14 +08:00
|
|
|
|
|
|
|
class DDS {
|
|
|
|
private:
|
2024-12-31 17:04:37 +08:00
|
|
|
phase_t phase = 0;
|
2024-12-30 23:45:14 +08:00
|
|
|
public:
|
2024-12-31 17:04:37 +08:00
|
|
|
phase_t ftw = 0;
|
2024-12-30 23:45:14 +08:00
|
|
|
double get() {
|
|
|
|
phase += ftw; // wraps on overflow
|
2024-12-31 17:04:37 +08:00
|
|
|
return sin(phase*2.0*M_PI/(double)PHASE_MAX);
|
2024-12-30 23:45:14 +08:00
|
|
|
}
|
2025-01-02 15:31:56 +08:00
|
|
|
phase_t get_phase() {
|
|
|
|
return phase;
|
|
|
|
}
|
2024-12-30 23:45:14 +08:00
|
|
|
};
|
|
|
|
|
2024-12-31 16:57:42 +08:00
|
|
|
template<typename T, unsigned int order>
|
2024-12-30 23:45:14 +08:00
|
|
|
class Lowpass {
|
|
|
|
private:
|
|
|
|
double k = 0.0;
|
2024-12-31 16:57:42 +08:00
|
|
|
T s[order] = {};
|
2024-12-30 23:45:14 +08:00
|
|
|
public:
|
|
|
|
void set_bandwidth(double f) {
|
|
|
|
k = 2.0*M_PI*f;
|
|
|
|
}
|
|
|
|
T update(T x) {
|
|
|
|
T a = x;
|
2024-12-31 16:57:42 +08:00
|
|
|
for(int i=0;i<order;i++) {
|
2024-12-30 23:45:14 +08:00
|
|
|
s[i] += (a - s[i])*k;
|
|
|
|
a = s[i];
|
|
|
|
}
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
};
|
2024-12-31 16:57:42 +08:00
|
|
|
|
|
|
|
template<unsigned int order>
|
|
|
|
class Lockin {
|
|
|
|
private:
|
2025-01-02 13:02:39 +08:00
|
|
|
double scale = 1.0;
|
2025-01-05 15:32:26 +08:00
|
|
|
phase_t multiplier = 1;
|
2024-12-31 17:04:37 +08:00
|
|
|
phase_t phase = 0;
|
2024-12-31 16:57:42 +08:00
|
|
|
Lowpass<std::complex<double>, order> lpf;
|
|
|
|
public:
|
2024-12-31 17:04:37 +08:00
|
|
|
phase_t ftw = 0;
|
2024-12-31 16:57:42 +08:00
|
|
|
void set_scale(double s) {
|
|
|
|
scale = s;
|
|
|
|
}
|
2025-01-05 15:32:26 +08:00
|
|
|
void set_multiplier(phase_t m) {
|
|
|
|
multiplier = m;
|
|
|
|
}
|
2024-12-31 16:57:42 +08:00
|
|
|
void set_bandwidth(double f) {
|
|
|
|
lpf.set_bandwidth(f);
|
|
|
|
}
|
|
|
|
std::complex<double> update(double x) {
|
|
|
|
std::complex<double> rotated;
|
2025-01-05 15:32:26 +08:00
|
|
|
rotated = x*std::polar(scale, multiplier*phase*2.0*M_PI/(double)PHASE_MAX);
|
2024-12-31 16:57:42 +08:00
|
|
|
phase -= ftw; // wraps on underflow
|
|
|
|
return lpf.update(rotated);
|
|
|
|
}
|
2025-01-02 15:31:56 +08:00
|
|
|
phase_t get_phase() {
|
|
|
|
return phase;
|
|
|
|
}
|
2024-12-31 16:57:42 +08:00
|
|
|
};
|
2025-01-18 15:46:45 +08:00
|
|
|
|
|
|
|
class Clocker {
|
|
|
|
private:
|
|
|
|
std::chrono::milliseconds period;
|
|
|
|
std::chrono::time_point<std::chrono::steady_clock> next_tick = std::chrono::time_point<std::chrono::steady_clock>::min();
|
|
|
|
public:
|
|
|
|
Clocker(std::chrono::milliseconds period): period(period) {};
|
|
|
|
void tick() {
|
|
|
|
if(next_tick == std::chrono::time_point<std::chrono::steady_clock>::min()) {
|
|
|
|
next_tick = std::chrono::steady_clock::now() + period;
|
|
|
|
} else {
|
|
|
|
auto duration = next_tick - std::chrono::steady_clock::now();
|
|
|
|
if(duration >= duration.zero())
|
|
|
|
std::this_thread::sleep_for(duration);
|
|
|
|
else
|
|
|
|
std::cerr << "missed tick" << std::endl;
|
|
|
|
next_tick += period;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|