#include #include #include #include #include #include #include #include #include #include #include "imgui.h" #include "imgui_impl_glfw.h" #include "imgui_impl_opengl3.h" #include "dsp_lib.hpp" #include "fifo.hpp" #include "kirdy.hpp" #define SND_BITS 24 #define SND_PCHAN 2 #define SND_RCHAN 2 #define SND_RATE 192000 #define SND_BUFLEN 4096 static std::atomic shutdown_threads; static std::atomic frequency[SND_PCHAN]; static std::atomic amplitude[SND_PCHAN]; #define HIST_DEPTH 512 static std::atomic peak; static std::atomic clipped; static std::atomic in_wave_trigger; static std::mutex in_wave_mutex; static float in_wave[HIST_DEPTH]; static std::atomic multiplier[SND_PCHAN]; static std::atomic lpf_bandwidth[SND_PCHAN]; static std::atomic li_mag[SND_PCHAN]; static std::atomic li_phase[SND_PCHAN]; static std::mutex li_hist_mutex; static std::atomic li_hist_hold[SND_PCHAN]; static float li_hist_mag[SND_PCHAN][HIST_DEPTH]; static float li_hist_phase[SND_PCHAN][HIST_DEPTH]; enum class InWaveState { delay, wait_trigger, capturing }; static void dsp_thread() { struct sio_hdl *hdl; struct sio_par par; hdl = sio_open(SIO_DEVANY, SIO_PLAY|SIO_REC, 1); if(hdl == nullptr) { std::cerr << "failed to open sound device\n"; return; } sio_initpar(&par); par.sig = 1; par.bits = SND_BITS; par.pchan = SND_PCHAN; par.rchan = SND_RCHAN; par.rate = SND_RATE; par.le = SIO_LE_NATIVE; par.xrun = SIO_ERROR; if(!sio_setpar(hdl, &par)) { std::cerr << "failed to set sound device parameters\n"; sio_close(hdl); return; } if(!sio_getpar(hdl, &par)) { std::cerr << "failed to get back sound device parameters\n"; sio_close(hdl); return; } if(par.sig != 1 || par.bits != SND_BITS || par.pchan != SND_PCHAN || par.rchan != SND_RCHAN || par.rate != SND_RATE || par.le != SIO_LE_NATIVE) { std::cerr << "sound device parameter mismatch\n"; sio_close(hdl); return; } if(!sio_start(hdl)) { std::cerr << "failed to start sound device\n"; sio_close(hdl); return; } DDS dds[SND_PCHAN]; int32_t buf_out[SND_BUFLEN*SND_PCHAN]; size_t buf_out_offset = sizeof(buf_out); int32_t buf_in[SND_BUFLEN*SND_RCHAN]; size_t buf_in_offset = sizeof(buf_in); FIFO ftw_fifo[SND_PCHAN]; int clipped_count = 0; double peak_running = 0.0; int peak_count = 0; phase_t last_trig_phase = 0; InWaveState in_wave_state = InWaveState::delay; int in_wave_count = 0; float in_wave_buf[HIST_DEPTH]; Lockin<4> lockin[SND_PCHAN]; for(int i=0;i= sizeof(buf_in)) { for(int i=0;i ftw = ftw_fifo[i].pull(); if(ftw.has_value()) lockin[i].ftw = ftw.value(); else std::cerr << "FTW FIFO underflow\n"; } buf_in_offset -= sizeof(buf_in); } for(int i=0;i 0.99*pow(2.0, SND_BITS-1)) // display the clipped indicator for about one second clipped_count = SND_RATE; } if(clipped_count > 0) { clipped_count--; clipped = true; } else clipped = false; double sample_abs = std::abs(sample); if(sample_abs > peak_running) peak_running = sample_abs; peak_count++; if(peak_count == SND_RATE/10) { peak_count = 0; peak = peak_running/(pow(2.0, SND_BITS-1)*SND_RCHAN); peak_running = 0.0; } phase_t trig_phase = lockin[in_wave_trigger].get_phase(); bool trigger = trig_phase > last_trig_phase; last_trig_phase = trig_phase; switch(in_wave_state) { case InWaveState::delay: in_wave_count++; if(in_wave_count == SND_RATE/10) in_wave_state = InWaveState::wait_trigger; break; case InWaveState::wait_trigger: if(trigger) { in_wave_count = 0; in_wave_state = InWaveState::capturing; } break; case InWaveState::capturing: in_wave_buf[in_wave_count++] = sample/(pow(2.0, SND_BITS-1)*SND_RCHAN); if(in_wave_count == HIST_DEPTH) { { std::lock_guard guard(in_wave_mutex); std::memcpy(in_wave, in_wave_buf, sizeof(in_wave)); } in_wave_count = 0; in_wave_state = InWaveState::delay; } break; } for(int j=0;j lockin_out = lockin[j].update(sample); double mag = std::abs(lockin_out); double phase = std::arg(lockin_out); li_mag[j] = mag; li_phase[j] = phase; if(!li_hist_hold[j]) { li_count[j]++; if(li_count[j] == 200) { li_count[j] = 0; std::lock_guard guard(li_hist_mutex); std::memmove(&li_hist_mag[j][0], &li_hist_mag[j][1], (HIST_DEPTH-1)*sizeof(float)); li_hist_mag[j][HIST_DEPTH-1] = mag; std::memmove(&li_hist_phase[j][0], &li_hist_phase[j][1], (HIST_DEPTH-1)*sizeof(float)); li_hist_phase[j][HIST_DEPTH-1] = phase; } } } } } if(sio_eof(hdl)) { std::cerr << "sound I/O error\n"; sio_close(hdl); return; } } sio_stop(hdl); sio_close(hdl); } static const char *kirdies[SND_PCHAN][2] = { {"192.168.1.128", "1337"}, {"192.168.1.126", "1337"}, }; static std::atomic servo_enable[SND_PCHAN]; static std::atomic servo_state[SND_PCHAN]; static std::atomic laser_temp[SND_PCHAN]; static std::atomic init_current_cooling[SND_PCHAN]; static std::atomic init_current_heating[SND_PCHAN]; static std::atomic init_temp[SND_PCHAN]; static std::atomic leadin_current[SND_PCHAN]; static std::atomic leadin_mag[SND_PCHAN]; static void servo_thread(int channel) { Clocker clocker = Clocker(std::chrono::milliseconds(100)); Kirdy kirdy = Kirdy(kirdies[channel][0], kirdies[channel][1]); float temp; while(true) { servo_state[channel] = "DISABLED"; while(!servo_enable[channel]) { clocker.tick(); if(shutdown_threads) return; laser_temp[channel] = temp = kirdy.get_laser_temp(); } servo_state[channel] = "INIT"; if(temp > init_temp[channel]) { while(servo_enable[channel] && temp > init_temp[channel]) { kirdy.set_tec_current(init_current_cooling[channel]); clocker.tick(); laser_temp[channel] = temp = kirdy.get_laser_temp(); } } else { while(servo_enable[channel] && temp < init_temp[channel]) { kirdy.set_tec_current(init_current_heating[channel]); clocker.tick(); laser_temp[channel] = temp = kirdy.get_laser_temp(); } } if(!servo_enable[channel]) continue; servo_state[channel] = "LEAD-IN"; while(servo_enable[channel] && li_mag[channel] < leadin_mag[channel]) { kirdy.set_tec_current(leadin_current[channel]); clocker.tick(); laser_temp[channel] = temp = kirdy.get_laser_temp(); } servo_state[channel] = "LOCKING"; while(servo_enable[channel]) { clocker.tick(); laser_temp[channel] = temp = kirdy.get_laser_temp(); } } } int main(int argc, char* argv[]) { if(!glfwInit()) { std::cerr << "failed to initialize GLFW\n"; return 1; } std::atexit(glfwTerminate); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); static GLFWwindow *window = glfwCreateWindow(1024, 1370, "Soundlocker", nullptr, nullptr); if(window == nullptr) { std::cerr << "failed to create GLFW window\n"; return 1; } static auto DestroyWindow = []() { glfwDestroyWindow(window); }; std::atexit(DestroyWindow); glfwMakeContextCurrent(window); glfwSwapInterval(1); IMGUI_CHECKVERSION(); ImGui::CreateContext(); static auto ImGuiDestroyContext = []() { ImGui::DestroyContext(); }; std::atexit(ImGuiDestroyContext); ImGuiIO& io = ImGui::GetIO(); io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; ImGui::StyleColorsDark(); ImGui_ImplGlfw_InitForOpenGL(window, true); std::atexit(ImGui_ImplGlfw_Shutdown); ImGui_ImplOpenGL3_Init("#version 130"); std::atexit(ImGui_ImplOpenGL3_Shutdown); for(int i=0;i guard(in_wave_mutex); ImGui::PlotLines("waveform", in_wave, HIST_DEPTH, 0, 0, -1.0f, 1.0f, ImVec2(0.0f, 200.0f)); } } if(ImGui::CollapsingHeader("Demodulation", ImGuiTreeNodeFlags_DefaultOpen)) { for(int i=0;i guard(li_hist_mutex); sprintf(str, "##magnitude%d", i); ImGui::PlotLines(str, li_hist_mag[i], HIST_DEPTH, 0, 0, -0.0f, 0.1f*plot_scale[i], ImVec2(0.0f, 200.0f)); sprintf(str, "##phase%d", i); ImGui::PlotLines(str, li_hist_phase[i], HIST_DEPTH, 0, 0, -M_PI, M_PI, ImVec2(0.0f, 200.0f)); } ImGui::PopItemWidth(); ImGui::Text("values: %8.5f %8.5f rad", (double)li_mag[i], (double)li_phase[i]); } } if(ImGui::CollapsingHeader("Laser servo", ImGuiTreeNodeFlags_DefaultOpen)) { for(int i=0;i