// file: $(NEDC_NFC)/class/cpp/Edf/edf_05.cc // // This file contains channel selection and manipulation methods. // // Revision History: // // 20240307 (JP): refactored code to expand set/get methods // // local include files // #include "Edf.h" //***************************************************************************** // // public methods: channel selection and manipulation // //***************************************************************************** // method: select_channel // // arguments: // VectorDouble& sigo: selected channel (output) // VVectorDouble& sigi: signal from file (input) // char* sstr: a character string containing a label string (input) // // return: a boolean value indicating status // // This method retrieves the signal data associated with a channel based on // the provided channel label. This method differs from select method // in the way that the latter modifies the header information. // bool Edf::select_channel(VVectorDouble& sigo_a, VVectorDouble& sigi_a, const char* sstr_a) { // selected match_mode // MATCH_MODE scmmode = smmode_d; // display debug information // if (dbgl_d > Dbgl::DETAILED) { fprintf(stdout, "%s (line: %d) %s: beginning channel selection [%s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, sstr_a); } // find the corresponding channel in the input signal // long pos; // get the position of desired channel label // if ((pos = Edf::find_match(sstr_a, hdr_ghdi_nsig_rec_d, hdr_chan_labels_d, scmmode)) < 0) { fprintf(stdout, "%s (line: %d) %s: no match for [%s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, sstr_a); return false; } if (dbgl_d > Dbgl::DETAILED) { fprintf(stdout, "%s (line: %d) %s: mapping channel %ld [%s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, pos, hdr_chan_labels_d[pos]); } // display debug information // if (dbgl_d > Dbgl::DETAILED) { fprintf(stdout, "%s (line: %d) %s: done with channel selection\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // create output space // long sigi_end = sigi_a.size(); // make sure it has the same size // Edf::resize(sigo_a, sigi_end, false); // create output space // sigi_end = sigi_a[0].size(); // make sure it has the same size // Edf::resize(sigo_a[0], sigi_end, false); // copy the data // for (long j = 0; j < sigi_end; j++) { sigo_a[0][j] = sigi_a[pos][j]; } // exit gracefully // return true; } // method: select // // arguments: // VVectorDouble& sigo: selected signal (output) // VVectorDouble& sigi: signal from file (input) // char* sstr: a character string containing a label string (input) // MATCH_MODE match_mode: match mode (input) // // return: a boolean value indicating status // // This method selects channels within a signal based on the provided // channel labels. The argument match_mode controls the precision with which // the labels are matched. // // Note that the arguments refer to labels coming from the parameter // file. The final signal is organized according to the order specified // in the montage specs in the parameter file. // // Note also that if the input "(null)", then the entire input // signal is selected. // bool Edf::select(VVectorDouble& sigo_a, VVectorDouble& sigi_a, const char* sstr_a, MATCH_MODE match_mode_a) { // declare local variables // bool status = true; // save the match mode // smmode_d = match_mode_a; // display debug information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: beginning channel selection [%s] [%ld]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, sstr_a, (long)smmode_d); } // mode "(null)": copy everything // if (strcmp(sstr_a, NULL_NAME) == 0) { return Edf::copy_signal(sigo_a, num_slabels_d, slabels_d, sigi_a, hdr_ghdi_nsig_rec_d, hdr_chan_labels_d); } // mode "everything else": process the channel selection field // else { // parse the channel selection field // status = Edf::parse_line(num_slabels_d, slabels_d, sstr_a, (char*)Itgl::COMMA); if (!status) { fprintf(stdout, "%s (line: %d) %s: error parsing channel tag [%s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, sstr_a); return false; } if (dbgl_d > Dbgl::DETAILED) { fprintf(stdout, "%s (line: %d) %s: parsed channel labels: [%s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, sstr_a); for (long i = 0; i < num_slabels_d; i++) { fprintf(stdout, "%s (line: %d) %s: %ld: [%s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, i, slabels_d[i]); } } // create output space // Edf::resize(sigo_a, num_slabels_d, false); // create space for information associated with the new channels // //long npos[num_slabels_d]; char* new_chan_labels[num_slabels_d]; char* new_chan_trans_types[num_slabels_d]; char* new_chan_phys_dim[num_slabels_d]; double new_chan_phys_min[num_slabels_d]; double new_chan_phys_max[num_slabels_d]; long new_chan_dig_min[num_slabels_d]; long new_chan_dig_max[num_slabels_d]; char* new_chan_prefilt[num_slabels_d]; long new_chan_rec_size[num_slabels_d]; // loop over the selected channels: // since each channel can have a different channel size, we need // to save the channel sizes and, of course, the labels, as we // go along. // for (long i = 0; i < num_slabels_d; i++) { // find the corresponding channel in the input signal // long pos; if ((pos = Edf::find_match(slabels_d[i], hdr_ghdi_nsig_rec_d, hdr_chan_labels_d, match_mode_a)) < 0) { fprintf(stdout, "%s (line: %d) %s: no match for [%s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, slabels_d[i]); return false; } if (dbgl_d > Dbgl::DETAILED) { fprintf(stdout, "%s (line: %d) %s: mapping chan %ld [%s] to chan %ld [%s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, pos, hdr_chan_labels_d[pos], i, slabels_d[i]); } // copy channel-specific information // new_chan_labels[i] = new char[strlen(hdr_chan_labels_d[pos]) + 1]; strcpy(new_chan_labels[i], hdr_chan_labels_d[pos]); new_chan_trans_types[i] = new char[strlen(hdr_chan_trans_types_d[pos]) + 1]; strcpy(new_chan_trans_types[i], hdr_chan_trans_types_d[pos]); new_chan_phys_dim[i] = new char[strlen(hdr_chan_phys_dim_d[pos]) + 1]; strcpy(new_chan_phys_dim[i], hdr_chan_phys_dim_d[pos]); new_chan_phys_min[i] = hdr_chan_phys_min_d[pos]; new_chan_phys_max[i] = hdr_chan_phys_max_d[pos]; new_chan_dig_min[i] = hdr_chan_dig_min_d[pos]; new_chan_dig_max[i] = hdr_chan_dig_max_d[pos]; new_chan_prefilt[i] = new char[strlen(hdr_chan_prefilt_d[pos]) + 1]; strcpy(new_chan_prefilt[i], hdr_chan_prefilt_d[pos]); new_chan_rec_size[i] = hdr_chan_rec_size_d[pos]; // create output space // long j_end = sigi_a[pos].size(); Edf::resize(sigo_a[i], j_end, false); // copy the data // for (long j = 0; j < j_end; j++) { sigo_a[i][j] = sigi_a[pos][j]; } } // adjust the header information: // note we have to do this after the channels were selected // to avoid corrupting the header while channels are being copied // for (long i = 0; i < num_slabels_d; i++) { Edf::resize(hdr_chan_labels_d[i], strlen(new_chan_labels[i]) + 1, false); strcpy(hdr_chan_labels_d[i], new_chan_labels[i]); Edf::resize(hdr_chan_trans_types_d[i], strlen(new_chan_trans_types[i]) + 1, false); strcpy(hdr_chan_trans_types_d[i], new_chan_trans_types[i]); Edf::resize(hdr_chan_phys_dim_d[i], strlen(new_chan_phys_dim[i]) + 1, false); strcpy(hdr_chan_phys_dim_d[i], new_chan_phys_dim[i]); hdr_chan_phys_min_d[i] = new_chan_phys_min[i]; hdr_chan_phys_max_d[i] = new_chan_phys_max[i]; hdr_chan_dig_min_d[i] = new_chan_dig_min[i]; hdr_chan_dig_max_d[i] = new_chan_dig_max[i]; Edf::resize(hdr_chan_prefilt_d[i], strlen(new_chan_prefilt[i]) + 1, false); strcpy(hdr_chan_prefilt_d[i], new_chan_prefilt[i]); hdr_chan_rec_size_d[i] = new_chan_rec_size[i]; } // clean up memory // Edf::cleanup(new_chan_labels, num_slabels_d); Edf::cleanup(new_chan_trans_types, num_slabels_d); Edf::cleanup(new_chan_phys_dim, num_slabels_d); Edf::cleanup(new_chan_prefilt, num_slabels_d); // update the number of channels and the header size - this is a really // critical step that preserves the integrity of the header // hdr_ghdi_nsig_rec_d = num_slabels_d; hdr_ghdi_hsize_d = compute_header_size(hdr_ghdi_nsig_rec_d); } // display debug information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: done with channel selection\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // exit gracefully // return true; } // method: remove // // arguments: // VVectorDouble& sigo: selected signal (output) // VVectorDouble& sigi: signal from file (input) // char* sstr: a character string containing a label string (input) // MATCH_MODE matmode: match mode (input) // // return: a boolean value indicating status // // This method removes channels within a signal based on the provided // channel labels. // bool Edf::remove(VVectorDouble& sigo_a, VVectorDouble& sigi_a, const char* sstr_a, MATCH_MODE match_mode_a) { // declare local variables // bool status = true; // save the match mode // smmode_d = match_mode_a; // display debug information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: beginning channel selection [%s] [%ld]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, sstr_a, (long)smmode_d); } // mode "(null)": copy everything // if (strcmp(sstr_a, NULL_NAME) == 0) { fprintf(stdout, "%s (line: %d) %s: can't remove all channels [%s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, sstr_a); return false; } // parse the channel selection field // status = Edf::parse_line(num_slabels_d, slabels_d, sstr_a, (char*)Itgl::COMMA); if (!status) { fprintf(stdout, "%s (line: %d) %s: error parsing channel tag [%s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, sstr_a); return false; } // build a list of channels to be saved by excluding those that // match the remove list. Note we use BSTR because these strings // can get very long. // char sstr[Itgl::MAX_BSTR_LENGTH]; memset(sstr, (int)0, Itgl::MAX_BSTR_LENGTH); long num_keep_labels = 0; for (long i = 0; i < hdr_ghdi_nsig_rec_d; i++) { // search for a match // long num_matches = 0; long num_tries = 0; while((num_tries < num_slabels_d) && (num_matches == 0)) { if (strstr(hdr_chan_labels_d[i], slabels_d[num_tries]) != (char*)NULL) { num_matches++; } num_tries++; } // add this to the output string // if (num_matches == 0) { if (num_keep_labels != 0) { strcat(sstr, Itgl::COMMA); strcat(sstr, Itgl::SPACE); } strcat(sstr, hdr_chan_labels_d[i]); num_keep_labels++; } } // display debug information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: done with channel selection\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // exit gracefully // return Edf::select(sigo_a, sigi_a, sstr, match_mode_a); } // method: apply_montage // // arguments: // VVectorDouble& sigo: selected signal (output) // VVectorDouble& sigi: signal from file (input) // char* mstr: a character string containing a montage label string (input) // MATCH_MODE match_mode: a value indicating the type of match (input) // // return: a boolean value indicating status // // This method computes a montage by differencing channels. Note that a // key design choice was that if the channel is not available in the // input signal, a warning message is printed and the output channel // is zeroed out. // bool Edf::apply_montage(VVectorDouble& sigo_a, VVectorDouble& sigi_a, char** mstr_a, MATCH_MODE match_mode_a) { // declare local variables // bool status = true; // display debug information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: beginning apply_montage\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // save the match mode // mmmode_d = match_mode_a; // check the mode // if (strcmp(mstr_a[0], Edf::NULL_NAME) == 0) { return Edf::copy_signal(sigo_a, num_mlabels_d, mlabels1_d, sigi_a, num_slabels_d, slabels_d); } // parse the montage string // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: parsing the montage string\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // create output space // if (num_mlabels_d != (long)sigo_a.size()) { Edf::resize(sigo_a, num_mlabels_d, false); } // loop over all channels // for (long i = 0; i < num_mlabels_d; i++) { // case 1: no differencing // if (strcmp(mlabels2_d[i],NULL_NAME) == (long)0) { // find the corresponding channel in the input signal // long pos; if ((pos = Edf::find_match(mlabels1_d[i], num_slabels_d, slabels_d, match_mode_a)) < 0) { // when a label is not found, always display a warning message // and arbitrarily set the channel index to 0. // fprintf(stdout, "%s (line: %d) %s: no match for [%s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, mlabels1_d[i]); } // swap the data // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: copying channel %ld [%s] " "to channel %ld\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, pos, mlabels1_d[i], i); } // create space // long j_end = sigi_a[0].size(); if (pos >= 0) { j_end = sigi_a[pos].size(); } if (j_end != (long)sigo_a[i].size()) { Edf::resize(sigo_a[i], j_end, false); } // copy the data: // zero out the data if the channel is not found // if (pos < 0) { for (long j = 0; j < j_end; j++) { sigo_a[i][j] = 0; } } else { for (long j = 0; j < j_end; j++) { sigo_a[i][j] = sigi_a[pos][j]; } } } // case 2: differencing // else { // find the corresponding channel numbers in the input signal // long pos1; if ((pos1 = Edf::find_match(mlabels1_d[i], num_slabels_d, slabels_d, match_mode_a)) < 0) { // when a label is not found, always display a warning message // and arbitrarily set the channel index to 0. // fprintf(stdout, "%s (line: %d) %s: no match for [%s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, mlabels1_d[i]); } long pos2; if ((pos2 = Edf::find_match(mlabels2_d[i], num_slabels_d, slabels_d, match_mode_a)) < 0) { fprintf(stdout, "%s (line: %d) %s: no match for [%s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, mlabels2_d[i]); } // difference the data // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: " "computing channel %ld [%s] -- channel %ld [%s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, pos1, mlabels1_d[i], pos2, mlabels2_d[i]); } // create space: // if one of the labels has not been found, use channel 0 and // write zeroes to the signal. // if ((pos1 < 0) || (pos2 < 0)) { pos1 = 0; pos2 = 0; } long j_end = sigi_a[pos1].size(); if (j_end != (long)sigo_a[i].size()) { Edf::resize(sigo_a[i], j_end, false); } // copy the data // for (long j = 0; j < j_end; j++) { sigo_a[i][j] = sigi_a[pos1][j] - sigi_a[pos2][j]; } } } // adjust only the channel labels: // note we have to do this after the channels were selected // to avoid corrupting the header while channels are being copied // for (long i = 0; i < num_mlabels_d; i++) { // write the label specified by the parameter file // Edf::resize(hdr_chan_labels_d[i], strlen(mchan_d[i]) + 1); strcpy(hdr_chan_labels_d[i], mchan_d[i]); } // update the number of channels and the header size - this is a really // critical step that preserves the integrity of the header // hdr_ghdi_nsig_rec_d = num_mlabels_d; hdr_ghdi_hsize_d = compute_header_size(hdr_ghdi_nsig_rec_d); // display debug information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: done with apply_montage\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // exit gracefully // return status; } // // end of file