#include #include #include #include #include #include #include #include #include #include #include #include #include #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 1024 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> ramc[SND_PCHAN]; static std::atomic principal_angle[SND_PCHAN]; static std::atomic li_principal[SND_PCHAN]; static std::atomic li_hist_hold[SND_PCHAN]; static std::mutex li_hist_mutex; static float li_hist_mag[SND_PCHAN][HIST_DEPTH]; static float li_hist_phase[SND_PCHAN][HIST_DEPTH]; static float li_hist_principal[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) - (std::complex)ramc[j]; double principal = std::real(lockin_out*std::polar(1.0, (double)principal_angle[j])); li_principal[j] = principal; if(!li_hist_hold[j]) { li_count[j]++; if(li_count[j] == 600) { 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] = std::abs(lockin_out); std::memmove(&li_hist_phase[j][0], &li_hist_phase[j][1], (HIST_DEPTH-1)*sizeof(float)); li_hist_phase[j][HIST_DEPTH-1] = std::arg(lockin_out); std::memmove(&li_hist_principal[j][0], &li_hist_principal[j][1], (HIST_DEPTH-1)*sizeof(float)); li_hist_principal[j][HIST_DEPTH-1] = principal; } } } } } 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", "1550"}, {"192.168.1.126", "1550"}, }; 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_thr[SND_PCHAN]; static std::atomic loop_setpoint[SND_PCHAN]; static std::atomic loop_bias[SND_PCHAN]; static std::atomic loop_p[SND_PCHAN]; static void servo_thread(int channel) { Clocker clocker = Clocker(std::chrono::milliseconds(30)); 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-1"; float a = std::copysign(1.0f, (float)leadin_thr[channel]); while(servo_enable[channel] && a*li_principal[channel] < a*leadin_thr[channel]) { kirdy.set_tec_current(leadin_current[channel]); clocker.tick(); laser_temp[channel] = temp = kirdy.get_laser_temp(); } servo_state[channel] = "LEAD-IN-2"; while(servo_enable[channel] && a*li_principal[channel] > a*leadin_thr[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]) { kirdy.set_tec_current(loop_bias[channel] + loop_p[channel]*(li_principal[channel] - loop_setpoint[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(1300, 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); std::complex new_ramc = std::polar(li_hist_mag[i][HIST_DEPTH-1], li_hist_phase[i][HIST_DEPTH-1]) + (std::complex)ramc[i]; ramc_mag[i] = std::abs(new_ramc); ramc_phase[i] = std::arg(new_ramc); } if(ramc_enable[i]) ramc[i] = std::polar(ramc_mag[i], ramc_phase[i]); else ramc[i] = { 0.0, 0.0 }; sprintf(str, "LPF BW##%d", i); float lpf_bandwidth_l = lpf_bandwidth[i]; ImGui::SliderFloat(str, &lpf_bandwidth_l, 0.5f, 200.0f); lpf_bandwidth[i] = lpf_bandwidth_l; sprintf(str, "principal angle##%d", i); float principal_angle_l = principal_angle[i]; ImGui::SliderFloat(str, &principal_angle_l, 0.0f, M_PI); principal_angle[i] = principal_angle_l; ImGui::AlignTextToFramePadding(); ImGui::Text("plot:"); ImGui::SameLine(); bool hold_l = li_hist_hold[i]; sprintf(str, "hold##%d", i); ImGui::Checkbox(str, &hold_l); li_hist_hold[i] = hold_l; ImGui::SameLine(); sprintf(str, "magnitude##%d", i); ImGui::RadioButton(str, &plot_sel[i], 0); ImGui::SameLine(); sprintf(str, "phase##%d", i); ImGui::RadioButton(str, &plot_sel[i], 1); ImGui::SameLine(); sprintf(str, "principal##%d", i); ImGui::RadioButton(str, &plot_sel[i], 2); ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); sprintf(str, "##plot%d", i); { std::lock_guard guard(li_hist_mutex); switch(plot_sel[i]) { case 0: ImGui::PlotLines(str, li_hist_mag[i], HIST_DEPTH, 0, 0, -0.0f, 0.02f, ImVec2(0.0f, 200.0f)); break; case 1: ImGui::PlotLines(str, li_hist_phase[i], HIST_DEPTH, 0, 0, -M_PI, M_PI, ImVec2(0.0f, 200.0f)); break; case 2: ImGui::PlotLines(str, li_hist_principal[i], HIST_DEPTH, 0, 0, -0.02f, 0.02f, ImVec2(0.0f, 200.0f)); break; } } ImGui::PopItemWidth(); } } if(ImGui::CollapsingHeader("Laser servo", ImGuiTreeNodeFlags_DefaultOpen)) { for(int i=0;i